首页 > 编程语言 >浅谈排序网络与并行排序算法

浅谈排序网络与并行排序算法

时间:2023-10-24 17:04:31浏览次数:48  
标签:浅谈 int 并行 算法 序列 排序 双调

对于普通的基于比较排序我们拥有一个复杂度下界 \(O(n\log n)\),然而如果我们允许并行计算的话,将得到一些复杂度更优秀的计算方法。

听到并行这个词许多人就会认为你有几个线程复杂度就除以几,所以线程堆得越多越好。但许多的算法问题都必须要满足你必须要算完 A 才能去计算 B,比如对于普通的前缀和算法,每一个位置的结果都依赖上一个位置,如果直接并行将不会对复杂度产生优化。所以一但允许并行我们还要重新去设计一些算法,此时我们更少地去关心计算网络的大小而是更多地去关心它的深度,一些原本不太优秀的算法在并行计算这方面表现得更加优秀。

OI 甚至 CP 中考察并行算法的题目并不多见,比如说这道考察了计算并行环三染色问题。

研究并行排序时,我们也同时会研究排序网络。排序网络问题与并行排序问题十分有关联性,因为许多排序网络更浅的算法更加易于并行。排序网络指得是不依赖排序数组具体的取值,只允许你使用比较器构造出一个操作序列,满足任意一个序列经过这些操作后都变得有序。比较器的操作定义为如果 \(a_x<a_y\) 则交换 \(a_x,a_y\) 的取值。一个排序网络的深度指的是如果相邻的若干次操作如果互不相干就可以划分到同一层那么它的最小层数,这直接影响了该排序网络的并行复杂度。冒泡排序算法可以直接建出一个大小为 \(O(n^2)\) 深度 \(O(n^2)\) 的网络,如果修改成对奇偶位置分别冒泡则可以做到深度为 \(O(n)\) 级别。

而下面的并行排序算法都是时间复杂度 \(O(n\log^2 n)\),可以建出深度为 \(O(\log^2 n)\) 的排序网络。这些算法都是分治算法,为了方便起见以下默认将序列长度扩充成 2 的整幂

双调排序算法

定义一个序列是双调的,当且仅当它能够分成一个非严格单调递增的前缀和非严格单调递减的后缀(即“单峰”),或者说序列经过循环移位后满足前面的条件。

双调排序的基本思路是将原序列排序成双调序列,然后将双调序列再排序成单调序列。

对于如何排序成双调序列,思路十分暴力:将原序列折成两半,前面一半递归用双调排序将排成递增序列,后面一半也双调排成递减序列。

而双调排序的骄傲之处在于,对于一个双调序列,我们可以只使用 \(O(\log n)\) 层比较器将其归并成一个单调序列。

有这么一个 Batcher 定理,它指出了如果将双调序列劈成两半,对应位置(即 \(i\) 与 \(i+\frac{n}{2}\))使用一次比较器后,原序列将对半分成两个双调序列,而且两边的值域不相交。那么我们只需要直接往两边递归下去就可以得到一个单调序列了。

奇偶归并算法

#include <cstdio>
#include <algorithm>
#include <functional>
using namespace std;
int read(){/*...*/}
const int INF=0x3f3f3f3f;
int a[1<<20];
void ConditionalSwap(int x,int y,bool fl=0){
	if(fl) swap(x,y);
	if(a[x]>a[y]) swap(a[x],a[y]);
}
void BitonicChange(int l,int r,bool fl){ //bitonic->monotonic
	if(l+1==r) return;
	int p=(r-l)>>1;
	for(int i=l;i<l+p;++i)
		ConditionalSwap(i,i+p,fl);
	BitonicChange(l,l+p,fl);
	BitonicChange(r-p,r,fl);
}
void BitonicSort(int l,int r,bool fl=0){ //random->bitonic->monotonic
	if(l+1==r) return;
	int p=(r-l)>>1;
	BitonicSort(l,l+p,fl^1);
	BitonicSort(r-p,r,fl);
	BitonicChange(l,r,fl);
}
void OddEvenMerge(int l,int r,int t){
	if(l+t==r) return;
	if(l+t+t==r) return ConditionalSwap(l,r-t);
	OddEvenMerge(l,r,t<<1);
	OddEvenMerge(l+t,r+t,t<<1);
	for(int i=l+t;i<r-t;i+=(t<<1)) ConditionalSwap(i,i+t);
}
void OddEvenSort(int l,int r){
	if(l+1==r) return;
	int p=(r-l)>>1;
	OddEvenSort(l,l+p);
	OddEvenSort(r-p,r);
	OddEvenMerge(l,r,1);
}
int main(){
	int len=read();
	for(int i=0;i<len;++i) a[i]=read();
	int n=1;
	while(n<len) n<<=1;
	for(int i=len;i<n;++i) a[i]=INF;
	OddEvenSort(0,n);
	// Or BitonicSort(0,n);
	for(int i=0;i<len;++i) printf("%d ",a[i]);
	putchar('\n');
	return 0;
}

标签:浅谈,int,并行,算法,序列,排序,双调
From: https://www.cnblogs.com/yyyyxh/p/sort_in_parallel.html

相关文章

  • C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例
    相关源码测试用例下载包括4个压缩包,初始代码,实现前缀和,实现前缀积,实现前缀异或。都是在前者的基础上修改的。原理长度为n的数组nums,共有n+1个以nums[0]开始的子数组。索引范围分别为[0,i),i取值区间[0,n]。preSum[i]记录子数组[0,i)的和。比如:nums={1,2,3,4},则preSum={0,1,3,6......
  • C++前缀和算法应用:和至少为 K 的最短子数组的原理、源码及测试用例
    题目给你一个整数数组nums和一个整数k,找出nums中和至少为k的最短非空子数组,并返回该子数组的长度。如果不存在这样的子数组,返回-1。子数组是数组中连续的一部分。示例1:输入:nums=[1],k=1输出:1示例2:输入:nums=[1,2],k=4输出:-1示例3:输入:nums=......
  • C++前缀和算法:构造乘积矩阵
    题目给你一个下标从0开始、大小为n*m的二维整数矩阵grid,定义一个下标从0开始、大小为n*m的的二维矩阵p。如果满足以下条件,则称p为grid的乘积矩阵:对于每个元素p[i][j],它的值等于除了grid[i][j]外所有元素的乘积。乘积对12345取余数。返回grid的乘积......
  • C++算法:二叉树的序列化与反序列化
    #题目序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列/反序列化算法执行逻......
  • C++算法前缀和的应用:得分最高的最小轮调的原理、源码及测试用例
    题目给你一个数组nums,我们可以将它按一个非负整数k进行轮调,这样可以使数组变为[nums[k],nums[k+1],…nums[nums.length-1],nums[0],nums[1],…,nums[k-1]]的形式。此后,任何值小于或等于其索引的项都可以记作一分。例如,数组为nums=[2,4,1,3,0],我们按k=2进行......
  • C++算法:数据流的中位数
    题目中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。例如arr=[2,3,4]的中位数是3。例如arr=[2,3]的中位数是(2+3)/2=2.5。实现MedianFinder类:MedianFinder()初始化MedianFinder对象。voidaddNum(int......
  • C++算法前缀和的应用:分割数组的最大值的原理、源码及测试用例
    分割数组的最大值二分过些天整理基础知识题目给定一个非负整数数组nums和一个整数m,你需要将这个数组分成m个非空的连续子数组。设计一个算法使得这m个子数组各自和的最大值最小。示例1:输入:nums=[7,2,5,10,8],m=2输出:18解释:一共有四种方法将nums分割为2个......
  • C++算法:给表达式添加运算符
    题目给定一个仅包含数字0-9的字符串num和一个目标值整数target,在num的数字之间添加二元运算符(不是一元)+、-或*,返回所有能够得到target的表达式。注意,返回表达式中的操作数不应该包含前导零。示例1:输入:num=“123”,target=6输出:[“1+2+3”,“123......
  • C++数位算法:数字1的个数
    题目给定一个整数n,计算所有小于等于n的非负整数中数字1出现的个数。示例1:输入:n=13输出:6示例2:输入:n=0输出:0提示:0<=n<=1092023年1月版classSolution{public:intcountDigitOne(intn){intiNum=0;intiMul=1;for(inti=0;i<9;i++){......
  • C++桶排序算法的应用:存在重复元素 III
    题目给你一个整数数组nums和两个整数indexDiff和valueDiff。找出满足下述条件的下标对(i,j):i!=j,abs(i-j)<=indexDiffabs(nums[i]-nums[j])<=valueDiff如果存在,返回true;否则,返回false。示例1:输入:nums=[1,2,3,1],indexDiff=3,valueDiff=0输出......