├── .gitignore ├── Makefile ├── README.md ├── dist ├── 01.html ├── 02-tree.gif ├── 02.html ├── 03-bad-tree.png ├── 03-color-table.png ├── 03-linked-list.png ├── 03-tree.png ├── 03.html ├── 04-1.jpg ├── 04-2.jpg ├── 04.html ├── 05-vm-00.jpg ├── 05-vm-01.jpg ├── 05.html └── prefix-minimum-wzf.png ├── scripts ├── build.sh └── watch.sh ├── src ├── 01.md ├── 02-tree.gif ├── 02.md ├── 03-bad-tree.png ├── 03-color-table.png ├── 03-linked-list.png ├── 03-tree.png ├── 03.md ├── 04-1.jpg ├── 04-2.jpg ├── 04.md ├── 05-vm-00.jpg ├── 05-vm-01.jpg ├── 05.md └── prefix-minimum-wzf.png └── static ├── github.css └── styles.css /.gitignore: -------------------------------------------------------------------------------- 1 | homework 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | ./scripts/build.sh 3 | watch: 4 | ./scripts/watch.sh 5 | pages: 6 | git push origin master:gh-pages 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Parallel Algorithm Notes 2 | 3 | This is our (mainly mine and Zhudhjen's) notes for Parallel Algorithms course. 4 | 5 | 作为一个外专业的弱菜,我决定建个笔记 repo 来帮助我进行思考。 6 | 希望能有一种持久的形式作为基础,毕竟“心智之中并无词典”。 7 | 8 | 欢迎大家来 pull request 或者提 issue。 9 | 10 | ## Special Thanks 11 | 12 | - 火龙果 13 | 14 | - xqy 15 | 16 | ## TOC 17 | 18 | 1. [Important factors, parallel summing, machine model and parallel min](http://zenozeng.github.io/parallel-algorithm-notes/dist/01.html) by Zhang Hai 19 | 20 | 2. [Prefix Sum and Parallel Sorting](http://zenozeng.github.io/parallel-algorithm-notes/dist/02.html) by Zeno Zeng, Zhudhjen and CSerxy 21 | 22 | 3. [Linked List](http://zenozeng.github.io/parallel-algorithm-notes/dist/03.html) by Zeno Zeng 23 | 24 | 4. [Lecture 04](http://zenozeng.github.io/parallel-algorithm-notes/dist/04.html) by CSerxy 25 | 26 | 5. [Lecture 05](http://zenozeng.github.io/parallel-algorithm-notes/dist/05.html) by Zeno Zeng 27 | 28 | ## Dev 29 | 30 | All source is written in Github Favored Markdown and converted to html using pandoc (with MathJax). 31 | 32 | ### Build 33 | 34 | ```bash 35 | make build 36 | ``` 37 | 38 | ### Watch 39 | 40 | ```bash 41 | make watch 42 | ``` 43 | 44 | ## Extra Links 45 | 46 | - [猴子排序 - 一种不实用的排序算法](http://zh.wikipedia.org/wiki/Bogo%E6%8E%92%E5%BA%8F) 47 | 48 | - BSR (Bit Scan Reverse in ASM) 49 | -------------------------------------------------------------------------------- /dist/01.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |Important factors:
17 |Number of processors
Time
Operation is the product of time and number of processors.
22 |Summing \(n\) numbers
24 |Divide and conquer with \(\frac{n}2\) processors. Then we can always do each step in parallel, so the time is the number of steps, \(log(n)\).
26 |for (i = 0, i < log(n), ++i) {
27 | for P % 2^(i+1) == 0 do in parallel {
28 | // P starts with 0.
29 | A[P] = A[P] + A[P+2^i];
30 | }
31 | }
Use less processors. Group by \(log(n)\) numbers, and use \(\frac{n}{log(n)}\) processors. Then we have one processor for each group, and we can sum each group sequentially in \(log(n)\) time, then use all the \(\frac{n}{log(n)}\) processors to add \(\frac{n}{log(n)}\) numbers divide and conquer in \(log(\frac{n}{log(n)})\) time. The overall time is still \(O(log(n))\).
Theorem for lower bound of time: A step with \(P\) processors in \(T\) time can be translated into a sequential processor in \(P\cdot{T}\) time.
35 |So for best sequential time \(T_1\), then \(P\cdot{T_P}\geq{T}_1\).
36 |So for summing, the sequential time is \(O(n)\) (in fact \(n-1\)), this is the lower bound for \(P\cdot{T}\)
37 |PRAM: Parallel Random Access Machine.
39 |EREW: Exclusive read exclusive write.
40 |CREW: Concurrent read exclusive write.
41 |CRCW: Concurrent read concurrent write.
42 |Common < arbitrary < priority
44 |arbitrary
priority (by process index)
common (have to write same value)
Find smallest number:
51 |Naive: \(n^2\) processors, set 1 on the larger number, return the number with 0. The time is constant.
Use less processors. Group by \(n^{\frac{1}{2}}\), each group uses \(n\) processors, altogether \(n^{\frac{3}{2}}\) processors. Then merge within constant time.
\(n\) numbers, \(n^{\frac{5}{4}}\) processors: Group by \(n^{\frac{1}{4}}\), the needed number of processors is just \((n^{\frac{1}{4}})^2\cdot{n}^{\frac{3}{4}}\). Then there are \(n^{\frac{3}{4}}\) numbers to merge, group by \(n^{\frac{3}{8}}\), the needed number of processors is \(n^{\frac{9}{8}}\), less than \(n^{\frac{5}{4}}\).
\(n\) numbers, \(n\) processors: Group by \(\sqrt{n}\), can be merged with available \(n\) processors; Subgroup by \(n^{\frac{1}{4}}\), can be merged with available \(\sqrt{n}\) processors, till the end. \(T(n,n)=1+T(n^{\frac{1}{2}},n^{\frac{1}{2}})=i+T(n^{\frac{1}{2^i}},n^{\frac{1}{2^i}})\). When \(n^{\frac{1}{2^i}}\) is constant \(c\), then \(i\) is \(log(log(n))\).
Maximum independent set: The maximum set of vertices with no connecting edges between them.
58 |See also: http://en.wikipedia.org/wiki/Prefix_sum
36 |假设我们有个数列:1, 2, 3, 4, 5, 6 其 prefix sum 数列应为:1, 3, 6, 10, 15, 21
39 |迭代算法见下:
40 |第 N 代 | 44 |\(a_0\) | 45 |\(a_1\) | 46 |\(a_2\) | 47 |\(a_3\) | 48 |\(a_4\) | 49 |\(a_5\) | 50 |
---|---|---|---|---|---|---|
0(原始数列) | 55 |1 | 56 |2 | 57 |3 | 58 |4 | 59 |5 | 60 |6 | 61 |
1(前两项和) | 64 |1 | 65 |1+2=3 | 66 |2+3=5 | 67 |3+4=7 | 68 |4+5=9 | 69 |5+6=11 | 70 |
2(前四项和) | 73 |1 | 74 |0*2+3=3 | 75 |0+1+5=6 | 76 |3+7=10 | 77 |5+9=14 | 78 |7+11=18 | 79 |
3(前八项和) | 82 |1 | 83 |0*6+3=3 | 84 |0*5+6=6 | 85 |0*4+10=10 | 86 |0*3+1+14=15 | 87 |3+18=21 | 88 |
该算法需要 \(n\) 个 processors,然后时间复杂度为 \(O(\log{n})\)
92 |注:对于位数不满足的情况,在前面 padding 0 就好了
93 |假设我们有 \(p\) 个处理器,其中 \(p<n\) 我们首先将 \(n\) 个数分为 \(p\) 个组 对于每一个组,我们使用一个处理器通过串行朴素算法进行计算其组内前缀和数组 \(s[g, i]\), 这一步的时间复杂度为 \(O(\frac{n}{p})\) 我们注意到,每一个组的数的和就是其最后一个元素的前缀和 我们得到了 \(p\) 个组的和,并且有 \(p\) 个处理器可以用于操作。 于是我们可以使用前文提到的算法算出组间的前缀和 \(S[g]\),这一步的时间复杂度为 \(O(\log p)\)。 最后我们更新每一个数的部分和: \[result[g, i] = s[g, i] + S[g-1]\] 这一步的时间复杂度为 \(O(\frac{n}{p})\)
95 |故其总体的时间复杂度为 \(O(\frac{n}{p}+\log p)\) ,达到了其时间复杂度下限,即summation的复杂度
96 |注:因为对于数列的最后一个数,其 prefix sum 的值即为数列的 sum, 所以 prefix sum 的时间复杂度下限即为 sum 运算。
97 |每一个节点都会收到一个信号值, 该节点将该信号值传递给左叶子, 然后将左叶子的值与信号值的和传递给右叶子。 当信号传递到底部的时候就可以得到预期的 \(a_{0k}\) 了。
99 |Tree
101 |此外,可以观察到的是,这棵树的横向尺度表达了其需要的 processors, 而其纵向尺度则表达了其需要的时间。
103 |注:\(a_{ij}\) 表示从 \(index = i\) 加到 \(index = j\)。
104 |大概有这样几种思路:
106 |算法一:对于每前 \(i\) 个数,使用 \(i^2\) 个核,在 \(O(1)\) 的时间复杂度内算出其最小值,处理器复杂度为: \[O(\sum_{i=1}^n{i^2})=O(n^3)\]
算法二:
109 |分成 \(n^{1/2}\) 组,每组有 \(n^{1/2}\) 个数,每组使用算法一进行计算,处理器复杂度为: \[O(n^{1/2}*(n^{1/2})^3)=O(n^2)\]
然后对这 \(n^{1/2}\) 个组的最小值计算前缀最小值,处理器复杂度为: \[(n^{1/2})^3=n^{1.5}\]
最后使用 \(n\) 个处理器对于每一个数的组内前缀和与组间前缀和中取较小值。
总处理器复杂度为\(O(n^2)\),时间复杂度为 \(O(1)\)
算法三:(思考题)
116 |首先确保时间复杂度为 \(O(1)\)
改进算法二:
119 |分成 \(g\) 组,第一步的处理器复杂度为 \(O(g*(n/g)^3)=O(n^3/g^2)\)
第二步的处理器复杂度为 \(O(g^3)\)
第三步的处理器复杂度仍为 \(O(n)\)
显然,当前两步使用处理器数量相同时,总处理器需要最少,故有: \[n^3/g^2=g^3\]
故当 \(g=n^{0.6}\) 时,处理器复杂度为 \(O(n^{1.8})\) ,这是一轮分组前缀最小值的最优情况。
然而如果我们在算法一种采取一轮分组算法来进行组内最小值计算,由于一轮分组的最小值算法的处理器复杂度为 \(O(n^{4/3})\) ,所以算法一的处理器复杂度改变为 \(O(n^{7/3})\) 。如果使用这个算法来进行最优化分组的一轮分组前缀最小值算法的第一步与第二步,那么最终的最小处理器复杂度为 \(O(n^{11/7})\)
所以说这里推到得到的算法很可能并不是最优的,因为按照这种思路可以持续进行优化,但时间复杂度有可能会提升,这不是我们想要的情况。
上课没有提到的情况:约束 \(p=n\)
上课没有提到的情况:约束 \(p<n\)
131 |CSerxy's Prefix Minimum
137 |Bucket Sorting
141 |对于 \(n\) 个 numbers,我们把它分成 \(\frac{n}{p}\) 组,每组有 \(p\) 个。 然后我们准备 types(数的种类数,这里我们假设为 \(\frac{n}{p}\) 种) 个桶,对于每个桶我们准备 \(p\) 个槽。 所以对于某个处理器#\(i\)正在处理的数 \(x\),我们首先基于 \(x\) 找到其对应的桶, 然后根据处理器的序号 \(i\) 找到对应的槽,放入。
142 |每个槽存的其实是 count 和指针。
Prefix Summing
144 |有了 count 之后我们做 prefix sum 就能得到其 index 范围。
Create Array
146 |在上一步中,我们得到了(一共 \(n\) 条):
147 |[
148 | { bucket: 0, slot: 0, index: 2},
149 | { bucket: 0, slot: 1, index: 5},
150 | { bucket: 1, slot: 0, index: 9},
151 | { bucket: 1, slot: 1, index: 12},
152 | ...
153 | ]
154 | 然后我们将所有每一条数据交给 processor 去做,如对第一条数据我们知道 index 为 2, 然后我们查之前的 count 数组里的数据,查到 count 为 2。 这样我们就知道 arr[0] 和 arr[1] 都应该是 0。
155 |注:slot#n 应交给 processor#n,这样保证了每个 processor 不会有超过 \(\frac{n}{p}\) 个数要处理。
\(T_1=O(n)\)
160 |\(T_p=O(\frac{n}{p}+\log{p})\)
161 |\(T_1=O(n)\)
163 |\(T_p=O(\frac{n}{p}+\log\log{n})\)
164 |\(T_1=O(n)\)
166 |Expecting (Randomize 算法可以实现,但是有时候会有 error): \[ 167 | T_p=O(\frac{n}{p}+\log n) 168 | \]
169 |当 \(n\) 远大于 \(p\) 时,此时由于 \(\log{n}\) 为小项,故消去,两式相等。
171 |当 \(p\) 较大时,由于实际只有 n 需要操作,所以两项还是相等。
172 |See also: #5
173 |Any range: \(\frac{n\log\log n}{p}+\log{n}\)
175 |{0, 1, ..., \(n^k\)}: \(\frac{n\log\log n}{p}\)
176 |\(n\frac{\sqrt{\log n}}{p}+\log{n}\)
178 | 179 | 180 | -------------------------------------------------------------------------------- /dist/03-bad-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenozeng/parallel-algorithm-notes/4e1bce4ef44764d7d0fdada3832d4d718fe9acb5/dist/03-bad-tree.png -------------------------------------------------------------------------------- /dist/03-color-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenozeng/parallel-algorithm-notes/4e1bce4ef44764d7d0fdada3832d4d718fe9acb5/dist/03-color-table.png -------------------------------------------------------------------------------- /dist/03-linked-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenozeng/parallel-algorithm-notes/4e1bce4ef44764d7d0fdada3832d4d718fe9acb5/dist/03-linked-list.png -------------------------------------------------------------------------------- /dist/03-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenozeng/parallel-algorithm-notes/4e1bce4ef44764d7d0fdada3832d4d718fe9acb5/dist/03-tree.png -------------------------------------------------------------------------------- /dist/03.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |linked list
37 |如图,现在假设我们有一个存在 array 中的链表, 然后我们需要将其依序存在 array 中(就是把这个链表弄弄直)。
39 |在线性算法中,我们需要 \(O(n)\) 的时间来完成这个需求。 现在我们需要并行地完成它, 你会马上想到利用我们前面学过的建树来解决这个问题, 但是如果对每个元素直接建树,你会发现每次只能缩减一个(如下图)。 这不是我们想要的。
40 |bad tree
42 |在之前的课中,我们利用二分的策略来完成我们的建树工作,而在链表中,相似的, 我们引入 pointer jumping 的概念:
45 |A[i].next = A[i].next.next;
46 | tree
48 |先看第一层,我们首先找到当前一个链表中的最大无关集(图中画了下滑线的点,后文我们会描述相关算法)。 找到最大无关集后我们对其和其 next 节点进行建树,由于是无关集, 所以保证了不会发生上面那个坏树的情况。 从第一层到第二层,我们可以发现相邻两个节点被合并, 而孤节点则直接被拉上来(通过 pointer jumping)。 这个过程我们称为 symmetry breaking。 重复这个过程,我们就能建出一颗树。
50 |每次合并我们缩减的数值为这个 maximal independent set 的数值。 在最坏情况下,我们取到的 maximal independent set 为 \(\frac{n}{3}\)。
51 |像这样:
52 |[\(a_0\), \(\underline{a_1}\), \(a_2\), \(a_3\), \(\underline{a_4}\), \(a_5\)]
53 |所以最坏情况下我们有 \(n_{i+1}=\frac{2}{3}n_i\), 所以这一部分我们需要 \(\log_{1.5}{n}\) 步数。
54 |注:由于是直接取指针,不是拷贝赋值,所以不要担心层迭代时的性能。
55 |注2:
56 |maximal independent set: could not add one more point
maximum independent set: number is max
如:
61 |[\(a_0\), \(\underline{a_1}\), \(a_2\), \(\underline{a_3}\), \(a_4\)] 是一个 maximal independent set,因为不能再加入任意一个点了,但它在数值上不是最大的。 maximum independent set 应该是 [\(\underline{a_0}\), \(a_1\), \(\underline{a_2}\), \(a_3\), \(\underline{a_4}\)]
62 |这里我们讨论的都是 maximal 的情况,因为 maximum 往往并不好找。
63 |现在假设我们有一个染好色的链表,一共三种颜色, 而且我们保证了相邻两个节点不会有相同的颜色:
66 | 69 | 70 |[\(\color{red} a_0\), \(\color{green} a_1\), \(\color{red} a_2\), \(\color{blue} a_3\), \(\color{red} a_4\), \(\color{green} a_5\), \(\color{blue} a_6\)]
71 |然后我们从里面取一个颜色,比如红色,就能取到一个无关集。
72 |[\(\color{red} \underline{a_0}\), \(\color{green} a_1\), \(\color{red} \underline{a_2}\), \(\color{blue} a_3\), \(\color{red} \underline{a_4}\), \(\color{green} a_5\), \(\color{blue} a_6\)]
73 |然后我们在对蓝色的每一个元素做检查,如果其相邻元素不在当前的 set 中,就插入。 这样我们就可以得到一个最大无关集。
74 |[\(\color{red} \underline{a_0}\), \(\color{green} a_1\), \(\color{red} \underline{a_2}\), \(\color{blue} a_3\), \(\color{red} \underline{a_4}\), \(\color{green} a_5\), \(\color{blue} \underline{a_6}\)]
75 |注:对同样的颜色,我们可以并行处理,因为同样的颜色必然不相邻,所以是安全的。
76 |Color Table
79 |显然,我们知道对一条链表的各个点的染色可以等价为对点间连线的染色问题。 那么我们要保证的就是让任意相邻的两条边颜色不一致。
81 |如图,我们在 \(3\) 和 \(4\) 之间画一条竖线, 那么对于所有 forward 过这条线的 link(比如 \(2\rightarrow5\) 和 \(3\rightarrow4\)), 我们染上一样的色。因为对于横跨同一条线的两条边,它们必然是相邻的。因为你想,如果这两条边相邻, 那么必然有两个点对到一个点,这在我们讨论的链表中不可能。 同理我们把所有 backward 过这条线的 link 染上另一种颜色。
82 |然后我们再四分之,对于各自 forward / backward 过线的 link 然后新的两种颜色。
83 |这样我们可以在 \(\frac{n}{p}\) 的时间内染好所有色,一共用掉 \(2\log{n}\) 种颜色。
84 |那么如何验证一条 link 是否 forward 或者 backward 一条分界线呢? 我们来看上面这张图。第一行是 array 的 index,下面是其二进制。 我们注意到对于跨过 \(3\) 和 \(4\) 的分隔线的两点,其最高位的 bit 不一致。 对于跨过 \(1\) 和 \(2\) 的分割线的两点,其最高位一致,但是次高位不一致。 \(0\) 和 \(1\) 之间的分割线同理。
85 |每次缩减尺寸之后,由于一些点被移除了,我们需要重新 pack array(就是把有效项移动到前面去), 这样我们才能继续使用我们之前所说的二分算法。否则时间上就只是 \(n\) 而不是 \(\frac{2}{3}n\)。 所以我们把需要保留的点标记为 1 而剩下的点标记为 0,然后做一次 prefix sum, 这样我们就得到了新的 array。
87 |Coloring: \(O(\frac{p}{n})\)
Sort nodes by color
Get maximal independent set
Merge nodes
04-01
16 |04-01
19 |这一部分具体可以看郭翀的视频。
17 |首先我们 coloring。
18 |首先我们 build 一个 table (\(\frac{n}{\log\log{n}}\) cols \(times\) \(\log\log{n}\) rows),把所有的点都丢进去。 然后一些点被 pair off,这样点就下来,有的快,有的慢,所以参差不齐。
19 |pair off 策略:
20 |If both are top nodes, storing the lower one
If both are top nodes, and same height, storing either one
The min weight is 1, the max weight is \(\log{n}\), the total weight is \(\frac{n}{\log\log{n}}(1+2+4+...)\)
25 |我们需要 \(\frac{n}{\log{n}}\) processors 和 \(\frac{n}{p}+\log{n}\) time。
26 |Pack: \(\frac{n}{p} + \log{n}\)。
27 |这个算法用于初步缩减 n 的数量使之小于 \(n\log{n}\)。 这样我们之后就可以用 pointer jumping。
28 |这里我们说的高度为其格数,然后其 weight 为 1, 2, 4, 8... 然后我们就可以证明经过一次 round,总 weight 至少减少 \(\frac{1}{4}\)。
29 |由于 \(weight > 1\),所以必然有 \(count(nodes) > \sum{weight}\)。
30 |假设我们有两个排好序的数列,他们方向相反,我们把他们接起来
32 |\[ 33 | 2 - 5 - 7 - 8 - 10 34 | \]
35 |\[ 36 | 12 - 4 - 0 37 | \]
38 |然后二分
39 |\[ 40 | \begin{xy} 41 | \xymatrix { 42 | 2 \ar@/^2pc/ [rrrrr] 43 | & 5 \ar@/^2pc/ [rrrrr] 44 | & 7 \ar@/^2pc/ [rrrrr] 45 | & 8 \ar@/^2pc/ [rrrrr] 46 | & | 47 | & 10 48 | & 12 49 | & 4 50 | & 0 \ar 51 | } 52 | \end{xy} 53 | \]
54 |交换,将小数置前
55 |\[ 56 | \begin{xy} 57 | \xymatrix { 58 | 2 59 | & 5 60 | & 4 61 | & 0 62 | & | 63 | & 10 64 | & 12 65 | & 7 66 | & 8 67 | } 68 | \end{xy} 69 | \]
70 |再对半分
71 |\[ 72 | \begin{xy} 73 | \xymatrix { 74 | 2 \ar@/^2pc/ [rrr] 75 | & 5 \ar@/^2pc/ [rrr] 76 | & | 77 | & 4 78 | & 0 79 | & | 80 | & 10 \ar@/^2pc/ [rrr] 81 | & 12 \ar@/^2pc/ [rrr] 82 | & | 83 | & 7 84 | & 8 85 | } 86 | \end{xy} 87 | \]
88 |交换,将小数置前
89 |\[ 90 | \begin{xy} 91 | \xymatrix { 92 | 2 93 | & 0 94 | & 4 95 | & 5 96 | & 7 97 | & 8 98 | & 10 99 | & 12 100 | } 101 | \end{xy} 102 | \]
103 |再二分
104 |\[ 105 | \begin{xy} 106 | \xymatrix { 107 | 2 \ar@/^2pc/ [r] 108 | & 0 109 | & 4 \ar@/^2pc/ [r] 110 | & 5 111 | & 7 \ar@/^2pc/ [r] 112 | & 8 113 | & 10 \ar@/^2pc/ [r] 114 | & 12 115 | } 116 | \end{xy} 117 | \]
118 |交换,将小数置前,这就是我们最终 merge 的结果了
119 |\[ 120 | \begin{xy} 121 | \xymatrix { 122 | 0 123 | & 2 124 | & 4 125 | & 5 126 | & 7 127 | & 8 128 | & 10 129 | & 12 130 | } 131 | \end{xy} 132 | \]
133 |我们需要 \(\log{n}\) steps,然后时间复杂度为 \(O(\frac{n}{p}\log{n}+\log{n})\)。
134 |假设我们有这个数组需要排序。
136 |\[ 137 | \begin{xy} 138 | \xymatrix { 139 | 7 \ar@/^2pc/ [r] 140 | & 5 141 | & 4 \ar@/^2pc/ [r] 142 | & 3 143 | & 8 \ar@/^2pc/ [r] 144 | & 6 145 | & 2 \ar@/^2pc/ [r] 146 | & 1 147 | } 148 | \end{xy} 149 | \]
150 |我们将对象分为4组,每组我们将左边当成是一个数列,右边当成一个数列,我们做 Bitonic Merging。 这样我们得到了:
151 |\[ 152 | 5 - 7 - | - 3 - 4 - || - 6 - 8 - | - 1 - 2 153 | \]
154 |然后我们将对象分为 2 组,每组我们将左边两个数当成一个数列,右边两个数当成一个数列。 我们再做 Bitonic Merging。
155 |这样有了:
156 |\[ 157 | 3 - 4 - 5 - 7 - | - 1 - 2 - 6 - 8 158 | \]
159 |我们再做一次 Bitonic Merging, 就得到了 Sorting 的结果。
160 |Bitonic Sorting 的时间复杂度为:\(O(\frac{n}{p}\log^2{n}+\log^2{n})\)。
161 |Hint: 如果需要简单的估算复杂度,可以直接取中间一段的最小值然后乘以那一段的 steps 作为下界。 对于上界,可以用最大复杂度乘以总体的 steps。
162 |现在假设我们有两条排好序的数列,长度分别为 \(n\) 和 \(m\),然后我们有 \(m+n\) 个 processors。
164 |对于 \(n\) 我们每 \(\sqrt{n}\) 取一个节点,一共取出 \(\sqrt{n}\) 个节点。 对于 \(m\) 我们每 \(\sqrt{m}\) 取一个节点,一共取出 \(\sqrt{m}\) 个节点。 对于每个点,我们将其与另一条线上的所有点相连。 这样一来我们一共得到了 \(\sqrt{mn}\) 条 links。
165 |\[ 166 | \newcommand{\node}[1]{ 167 | \enclose{circle}[mathcolor="black", padding="10px", thickness="1px"]{\color{black}{#1}} 168 | } 169 | \]
170 |\[ 171 | \begin{xy} 172 | \xymatrix { 173 | \node{0} 174 | & \node{\sqrt{n}} \ar[ld] \ar[d] \ar[rd] \ar[rrd] \ar[rrrd] 175 | & \node{2\sqrt{n}} 176 | & \node{3\sqrt{n}} 177 | & \node{4\sqrt{n}} 178 | &... 179 | \\ 180 | \node{0} 181 | & \node{\sqrt{m}} 182 | & \node{2\sqrt{m}} 183 | & \node{3\sqrt{m}} 184 | & \node{4\sqrt{m}} 185 | &... 186 | } 187 | \end{xy} 188 | \]
189 |对每条边我们比较两边的大小。这样一共需要计算 \(\sqrt{mn}\) 次,通过不等式, 我们很容易证明 \(\sqrt{mn} < m + n\),所以处理器肯定是够用的。
190 |\[ 191 | \begin{xy} 192 | \xymatrix { 193 | \node{0} 194 | & \node{\sqrt{n}} \ar[ld] \ar[d] \ar[rd] \ar[rrd] \ar[rrrd] 195 | & \node{2\sqrt{n}} 196 | & \node{3\sqrt{n}} 197 | & \node{4\sqrt{n}} 198 | &... 199 | \\ 200 | \node{0} 201 | & \node{\sqrt{m}} 202 | & \node{2\sqrt{m}} 203 | & \node{3\sqrt{m}} 204 | & \node{4\sqrt{m}} 205 | &... 206 | \\ 207 | 0 & 0 & 0 & 1 & 1 & ... 208 | } 209 | \end{xy} 210 | \]
211 |我们比较结果,如果小于则写 0 在那条边上,如果大于则写 1 在那条边上。 对于一个节点其第一个 0 - 1 交替的位置就是我们要找的位置。 这样我们知道将 \(\sqrt{n}\) 这个节点要被插在 \(2\sqrt{m}\) 和 \(3\sqrt{m}\) 之间。
212 |接下来我们要知道它具体要插在 \(2\sqrt{m}\) 和 \(3\sqrt{m}\) 之间哪个位置。 所以我们将 \(\sqrt{n}\) 这个节点的值与 \(2\sqrt{m}\) 和 \(3\sqrt{m}\) 之间所有 \(\sqrt{m}\) 个值进行比较。 而我们一共有 \(\sqrt{n} \times \sqrt{m} + \sqrt{m} \times \sqrt{n}\) 次计算(课上说的好像是\(m+n\)?但是我和郭翀都觉得是 \(2\sqrt{mn}\))要进行, 所以我们的处理器还是够的。
213 |要注意的是,我们不能只做一边的, 只有两边都做了,我们才能确定双向范围。
214 |还有要注意的是,我们每次 round 都只是标记(来限定范围)而已, 只有所有 round 结束了我们才会发生具体的操作。
215 |经过上一步,我们初步对这 \(\sqrt{m}\) 和 \(\sqrt{n}\) 段进行了 merge。 接下来我们对每一条之前 link 对应的 \(\sqrt{m} + \sqrt{n}\) 个数据, 我们已经知道了这 \(\sqrt{m} + \sqrt{n}\) 的整体位置。
216 |接下来,我们做和上一步一样的事情。这次一共有 \(\sqrt{mn}\) 组, 每组有 \(\sqrt{m} + \sqrt{n}\) 个数据。每组需要的处理器数是 \(2(mn)^{\frac{1}{4}}\)。 所以处理器还是够的。
217 |这样不断迭代,一共 \(\log{n}\) 代,我们就完成了整体的 merge。
218 |要注意的是我们不可能出现交叉的情况: \[ 219 | \begin{xy} 220 | \xymatrix { 221 | A \ar[rd] & B \\ 222 | C \ar[ru] & D 223 | } 224 | \end{xy} 225 | \] 因为我们两个数列都是排好序的,已知了 \(A < B\),\(C < D\)。 如果有 \(B < C\) 则有 \(B < D\),则 \(A\rightarrow{D}\) 这条 link 并不该存在。
226 |时间复杂度为:\(O(\frac{n}{p}\log\log{n} + \log\log{n})\)
227 |就是将利用之前 Bitonic Sorting 所述的方法,将 Merging 转换为 Sorting。
229 |时间复杂度为:\(O(\frac{n}{p}\log{n}\log\log{n} + \log{n}\log\log{n})\)
230 | 231 | 232 | -------------------------------------------------------------------------------- /dist/prefix-minimum-wzf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenozeng/parallel-algorithm-notes/4e1bce4ef44764d7d0fdada3832d4d718fe9acb5/dist/prefix-minimum-wzf.png -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p dist 4 | 5 | function build() { 6 | echo "building $1.md to dist/$1.html" 7 | pandoc "src/$1.md" -s --highlight-style pygments -c '../static/github.css' -c '../static/styles.css' --mathjax='//zenozeng.github.io/MathJax/init.js' -o "dist/$1.html" 8 | } 9 | 10 | for file in src/*.md 11 | do 12 | file=$(echo $file | sed 's/src\///' | sed 's/\.md//') 13 | build $file 14 | done 15 | 16 | cp src/*.gif dist 17 | cp src/*.png dist 18 | cp src/*.jpg dist 19 | -------------------------------------------------------------------------------- /scripts/watch.sh: -------------------------------------------------------------------------------- 1 | while true; do 2 | ./scripts/build.sh 3 | inotifywait -e modify src 4 | done 5 | -------------------------------------------------------------------------------- /src/01.md: -------------------------------------------------------------------------------- 1 | # Lecture 1 2 | 3 | ## Parallel Algorithm 4 | 5 | Important factors: 6 | 7 | 1. Number of processors 8 | 9 | 2. Time 10 | 11 | Operation is the product of time and number of processors. 12 | 13 | ## Example 1 14 | 15 | Summing $n$ numbers 16 | 17 | 1. Divide and conquer with $\frac{n}2$ processors. Then we can always do each step in parallel, so the time is the number of steps, $log(n)$. 18 | 19 | ``` 20 | for (i = 0, i < log(n), ++i) { 21 | for P % 2^(i+1) == 0 do in parallel { 22 | // P starts with 0. 23 | A[P] = A[P] + A[P+2^i]; 24 | } 25 | } 26 | ``` 27 | 28 | 2. Use less processors. Group by $log(n)$ numbers, and use $\frac{n}{log(n)}$ processors. Then we have one processor for each group, and we can sum each group sequentially in $log(n)$ time, then use all the $\frac{n}{log(n)}$ processors to add $\frac{n}{log(n)}$ numbers divide and conquer in $log(\frac{n}{log(n)})$ time. The overall time is still $O(log(n))$. 29 | 30 | Theorem for lower bound of time: A step with $P$ processors in $T$ time can be translated into a sequential processor in $P\cdot{T}$ time. 31 | 32 | So for best sequential time $T_1$, then $P\cdot{T_P}\geq{T}_1$. 33 | 34 | So for summing, the sequential time is $O(n)$ (in fact $n-1$), this is the lower bound for $P\cdot{T}$ 35 | 36 | ## Model 37 | 38 | PRAM: Parallel Random Access Machine. 39 | 40 | EREW: Exclusive read exclusive write. 41 | 42 | CREW: Concurrent read exclusive write. 43 | 44 | CRCW: Concurrent read concurrent write. 45 | 46 | ### Conflict-resolution 47 | 48 | Common < arbitrary < priority 49 | 50 | - arbitrary 51 | 52 | - priority (by process index) 53 | 54 | - common (have to write same value) 55 | 56 | ## Example 2 57 | 58 | Find smallest number: 59 | 60 | 1. Naive: $n^2$ processors, set 1 on the larger number, return the number with 0. The time is constant. 61 | 62 | 2. Use less processors. Group by $n^{\frac{1}{2}}$, each group uses $n$ processors, altogether $n^{\frac{3}{2}}$ processors. Then merge within constant time. 63 | 64 | 3. $n$ numbers, $n^{\frac{5}{4}}$ processors: Group by $n^{\frac{1}{4}}$, the needed number of processors is just $(n^{\frac{1}{4}})^2\cdot{n}^{\frac{3}{4}}$. Then there are $n^{\frac{3}{4}}$ numbers to merge, group by $n^{\frac{3}{8}}$, the needed number of processors is $n^{\frac{9}{8}}$, less than $n^{\frac{5}{4}}$. 65 | 66 | 4. $n$ numbers, $n$ processors: Group by $\sqrt{n}$, can be merged with available $n$ processors; Subgroup by $n^{\frac{1}{4}}$, can be merged with available $\sqrt{n}$ processors, till the end. $T(n,n)=1+T(n^{\frac{1}{2}},n^{\frac{1}{2}})=i+T(n^{\frac{1}{2^i}},n^{\frac{1}{2^i}})$. When $n^{\frac{1}{2^i}}$ is constant $c$, then $i$ is $log(log(n))$. 67 | 68 | Maximum independent set: The maximum set of vertices with no connecting edges between them. 69 | 70 | 6. When we have an algorithm using $P$ processors running in $T_P$ time, then we can simulate this algorithm with $pn$, since we have $log(log(n))$ for $n$ and $1$ for $n^{\frac{5}{4}}$.
71 |
--------------------------------------------------------------------------------
/src/02-tree.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenozeng/parallel-algorithm-notes/4e1bce4ef44764d7d0fdada3832d4d718fe9acb5/src/02-tree.gif
--------------------------------------------------------------------------------
/src/02.md:
--------------------------------------------------------------------------------
1 | # Lecture 02
2 |
3 | ## The Prefix sum problem
4 |
5 | See also: [http://en.wikipedia.org/wiki/Prefix_sum](http://en.wikipedia.org/wiki/Prefix_sum)
6 |
7 | ## Prefix Sum
8 |
9 | ### processors = n
10 |
11 | 假设我们有个数列:1, 2, 3, 4, 5, 6
12 | 其 prefix sum 数列应为:1, 3, 6, 10, 15, 21
13 |
14 | 迭代算法见下:
15 |
16 | 第 N 代 | $a_0$ | $a_1$ | $a_2$ | $a_3$ | $a_4$ | $a_5$ |
17 | -|-|-|-|-|-|-|-|-
18 | 0(原始数列) | 1 | 2 | 3 | 4 | 5 | 6
19 | 1(前两项和) | 1 | 1+2=3 | 2+3=5 | 3+4=7 | 4+5=9 | 5+6=11
20 | 2(前四项和) | 1 | 0\*2+3=3 | 0+1+5=6 | 3+7=10 | 5+9=14 | 7+11=18
21 | 3(前八项和) | 1 | 0\*6+3=3 | 0\*5+6=6 | 0\*4+10=10 | 0\*3+1+14=15 | 3+18=21
22 |
23 |
24 | 该算法需要 $n$ 个 processors,然后时间复杂度为 $O(\log{n})$
25 |
26 | 注:对于位数不满足的情况,在前面 padding 0 就好了
27 |
28 | ### processors < n
29 |
30 | 假设我们有 $p$ 个处理器,其中 $p