分治算法
例题
大整数乘法
大整数:超过计算机能直接表示和处理的整数
设 X 和 Y 为大整数,均为 n 未。
X 可二进制拆分为两部分,前 0 ~ n/2 位为 A,n/2 + 1 ~ n 位为 B。Y 同理拆为 C 和 D 两部分。
故:
\begin{eqnarray*}
X &=& A \times 2^{n/2} + B\\
Y &=& C \times 2^{n/2} + D\\
XY &=& (A \times 2^{n/2} + B)(C \times 2^{n/2} + D)\\
&=& AC\times2^n+AD\times2^{n/2}+BC\times2^{n/2}+BD\\
&=& AC\times2^n+(AD+BC)\times2^{n/2}+BD
\end{eqnarray*}
需要进行 4 次 2n/2 位乘法 AC; AD; BC; BD,以及 2 次移位(×2k 可用移位达成)、3 次加法操作。
得到时间复杂度的计算公式:
T(n)={O(1)4T(n/2)+O(n)n=1n>1
时间复杂度计算:
\begin{eqnarray*}
T(n) &=& 4T(n/2) + O(n)\\
&=& 4(4T(n/4)+ O (n/2))+O(n)\\
&=& 4(4(4T(n/8)+O(n/4)) + O (n/2))+O(n)\\
&&...\\
&=& 4^{\log{n}}O(1) \\
&=& n^2O(1)\\
&=& O(n^2)
\end{eqnarray*}
与使用竖式乘法的时间复杂度一样,但是 XY=AC×2n+(AD+BC)×2n/2+BD 可以进行变形减少乘法次数,将 $ (AD+BC) $ 变形为 (A−B)(D−C)+BD+AC,乍一看乘法从原来的 2 次变为了 3 次,但是 AC ; BD 在其他项中也有出现,所以实际上是将 2 次乘法变成了 1 次乘法,整条式子变成了 3 次乘法,故:
T(n)={O(1)3T(n/2)+O(n)n=1n>1
计算后得到:
\begin{eqnarray*}
T(n) &=& 3T(n/2) + O(n)\\
&=& 3(3T(n/4)+ O (n/2))+O(n)\\
&=& 3(3(3T(n/8)+O(n/4)) + O (n/2))+O(n)\\
&&...\\
&=& 3^{\log{n}}O(1) \\
&=& O(3^{\log{n}})\\
&=& O(n^{\log3})
\end{eqnarray*}
时间复杂度得到改进。
快速排序
递归加分治的经典排序算法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| int Partition(vector<int> &arr, int start_index, int end_index) { int left_index = start_index, right_index = end_index + 1; while(true) { while (arr[++left_index] < arr[start_index] && left_index <= end_index); while (arr[--right_index] > arr[start_index]); if (right_index <= left_index) break; swap(arr[left_index], arr[right_index]); }
swap(arr[right_index], arr[start_index]);
return right_index; }
void QuickSort (vector<int> &arr, int start_index, int end_index) { if (start_index < end_index) { int p = Partition(arr, start_index, end_index); QuickSort(arr, start_index, p-1); QuickSort(arr, p + 1, end_index); } }
|
随机基准划分:期望获得较为对称的划分。
Partition
函数不变,在外包裹一层函数,用于将随机基准与队首元素互换。QuickSort
函数调用该外层函数进行划分即可。
1 2 3 4 5
| int RandomPartition(vector<int> &arr, int start_index, int end_index) { int rand_index= rand() % (end_index - start_index + 1) + start_index; swap(arr[start_index], arr[rand_index]); return Partition(arr, start_index, end_index); }
|
随机选择算法(选择最 k 小的元素)
在快速排序的 Partition
函数的基础上,划分后的数组基准之右的元素均大于基准之左的元素,若左边的元素数量小于 k,则代表目标数一定在右边的元素中,反之则在左边的元素中,并以此选择一边继续划分,直至左边的长度为 k - 1 为止。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int RandomSelect(vector<int> &arr, int start_index, int end_index, int k) { int partition_index = RandomPartition(arr, start_index, end_index); int left_length = partition_index - start_index; if (left_length == k - 1) return arr[partition_index]; if (left_length >= k) { return RandomSelect(arr, start_index, partition_index, k); } else { return RandomSelect(arr, partition_index + 1, end_index, k - left_length + 1); } }
|