Intro
这个版本的基数排序和算法导论的不太相同,思想都是一样的,但是感觉比那个好理解很多
选用unsigned int 也就是u32 作为例子,按位来,一共只有两个桶,所以要比32次。但其实基数排序非常有意思。
在于这些都是可变的
设待排序列为n个记录,d个关键码,关键码的取值范围为radix,排序时间复杂度为O(d(n+radix)),
空间复杂度2n
这里n ∈[1e6,1e9) 做了下测试,d=32(因为32bit) ,radix = 2
思路一共两条,最高位优先MSD和最低位优先LSD,都是按位分桶嘛,也就是一个从头往尾排,一个从尾往头排。两个都可以,但是MSD会有点麻烦,比完高位要是一样还得再比一下低位,要分出很多子序列。而且并没有感受到有什么特别的优点,所以就LSD了
算法流程
先设一个tmp数组,也就是桶。但是因为按位来,0和1可以是一个大桶里的两个小桶
从最低位开始,0放一桶,1放一桶,0从tmp的第0位开始装,1从最后一位装。
装完合并回data,但是注意1那个桶的顺序要弄正常一点,之前是倒着装的。
合并的时候就是0放前1放后,这样最后是升序的
讨论
编程
这个之前学排序都是用泛型写的,基数排序不是很容易泛型。如果不是unsigned , 就要对符号位单独处理。如果是浮点类型,也可以用基数排序!!!之前被人怼了,当时没学IEEE浮点标准。肯定要对尾数和阶码作特殊处理,我懒得搞了网上有论文。
但是这说明一个道理!如果想兼容之前的排序代码,就要用模板特化!
template<typename T>
void radixSort(T* data,int left,int right){}
template<> void radixSort<u32>(u32* data,int left, int right)
{
//implement
}
也就是说,对不同的typename都应该分别实现不同的方法,调用的时候会调用对应的函数。
时间复杂度与空间复杂度
O(d(n+radix))这个在intro里面提了一下,可以稍微简化一下看一下效果,忽略太小的:
O(d*n) 看着是线性的,d一般不会太大,这里的例子是32,但是快排nlgn的lgn不一定就比d大。这样分析的话,数组长度要大于1<<32之后才会看到效果。
但是,基数排序是一个比较容易并行的排序,并行之后还可以大概除一个常数(代码要写的非常好才可以把大概去掉)这就可能会比快排好啦。
实验
就是vanilla quick sort 和radix sort比。
#数组长度1e7
quick sort 1.04497
radix sort 1.65082
#数组长度1e8
quick sort 11.9672
radix sort 16.6019
#数组长度1e9
quick sort 133.076
# 爆内存啦
为什么会爆内存呢?u32是4byte,1e9个u32也就是4GB!空间复杂度2N刚好我内存是8GB。然后就只能重启了= =
这样的话是不如快排快的,改进思路就是换基数和CUDA并行
主要代码
template<typename T>
void radixSort(T* data,int left,int right){}
template<> void radixSort<u32>(u32* data,int left, int right)
{
const int len = right - left + 1;
u32* tmp = new u32[len];
for(u32 bit = 0;bit < 32;bit++)
{
u32 cnt0 = 0;
u32 cnt1 = 0;
for(u32 i = 0;i < len;i++)
{
const u32 d = data[i];
const u32 bit_mask = (1<<bit);
if(d & bit_mask)
tmp[cnt1++] = d;
else
tmp[len - cnt0++ - 1] = d;
}
for(u32 i = 0;i < cnt0;i++)
data[i] = tmp[len - i -1];
for(u32 i = 0;cnt0 < len;i++)
data[cnt0++] = tmp[i];
}
delete[] tmp;
}
全部代码
#include<thread>
#include<atomic>
#include<ctime>
#include<iostream>
#include<ctime>
#include<cstdlib>
#include<algorithm>
using namespace std;
using u32 = unsigned int;
using TYPE = u32;
const int LEN =(int)1e6;
template<typename T=int>
void disp(T* data,int l)
{
// cout<<__func__<<": ";
for(int i = 0;i<l;i++)
{
if((i!=0) && (i%10==0))
cout<<endl;
cout<<data[i]<<" ";
}
cout<<endl;
}
template<typename T=int>
void init(T* data,int len)
{
srand(unsigned(time(0)));
for(int i = 0;i<len;i++)
data[i] = (T)rand();
}
template<typename T=int>
void bubbleSort(T* data,int left = 0,int right = LEN-1)
{
for(int i = left; i <= right; i++)
for (int j = i + 1; j <= right; j++)
if (data[i] > data[j])
swap(data[i], data[j]);
}
template<typename T=int>
void qsort(T* data,int left = 0,int right = LEN-1)
{
if(left < right)
{
int l = left;
int h = right;
T pivot = data[left];
while(l<h)
{
while(data[h] >= pivot && l < h) h--;
data[l] = data[h];
while(data[l]<pivot && l < h) l++;
data[h] = data[l];
}
data[l] = pivot;
qsort(data,left,l-1);
qsort(data,l+1,right);
}
}
bool Sorted = false;
template<typename T=int>
void sortswap(T* data,int index,int right)
{
if(index == right)
return;
if(data[index]>data[index+1])
{
swap(data[index],data[index+1]);
Sorted = false;
}
}
template<typename T=int>
void oddEvenSort(T* data,int left = 0,int right = LEN-1)
{
int latch = (right-left)/2 + (right-left)%2;
thread threads[latch];
int start = left;
while(!Sorted)
{
Sorted = true;
for(int i = start,j = 0;j<latch;i+=2,j++)
threads[j] = thread(sortswap<T>,data,i,right);
for(int i = 0;i<latch;i++)
threads[i].join();
if(start == left)
start = left +1;
else
start = left;
}
}
template<typename T>
void radixSort(T* data,int left,int right){}
template<> void radixSort<u32>(u32* data,int left, int right)
{
const int len = right - left + 1;
u32* tmp = new u32[len];
for(u32 bit = 0;bit < 32;bit++)
{
u32 cnt0 = 0;
u32 cnt1 = 0;
for(u32 i = 0;i < len;i++)
{
const u32 d = data[i];
const u32 bit_mask = (1<<bit);
if(d & bit_mask)
tmp[cnt1++] = d;
else
tmp[len - cnt0++ - 1] = d;
}
for(u32 i = 0;i < cnt0;i++)
data[i] = tmp[len - i -1];
for(u32 i = 0;cnt0 < len;i++)
data[cnt0++] = tmp[i];
}
delete[] tmp;
}
template<typename T>
void (*f[])(T*,int,int) =
{
qsort,
//bubbleSort,
//oddEvenSort,
radixSort
};
int main()
{
TYPE* data = new TYPE[LEN];
for(int i = 0;i<2;i++)
{
init<TYPE>(data,LEN);
auto t1 = clock();
f<TYPE>[i](data,0,LEN-1);
cout<<(double)(clock()-t1)/CLOCKS_PER_SEC<<endl;
//disp(data,100);
}
delete[] data;
return 0;
}