我眼中的快速排序.

knate 2010-06-20 10:01:03
基本上在这个版块里,每隔一个月,就会有一个讨论排序算法的主题飘上来.
几乎是每个月都来的东西了.
似乎这个月没来,不能破坏这个传统,是吧.
^_^

反正这个我只是当成学习笔记来记的.
喜欢的随便看看吧.

PS:
后面的代码有部分是网上不知道哪里找来的,年代久远了,已经找不到连接的.
只能对原作者说抱歉了.

不废话了,以下是检测函数用的代码.


#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <ctime>
#include <algorithm>

using namespace std;

typedef unsigned int VT;
typedef unsigned int* pVT;
typedef unsigned int SIZE_T ;

template<class T>
void swap_kn(T & a,T & b){
T _t = a;
a = b;
b = _t;
};


typedef unsigned __int64 CTIME;

inline unsigned __int64 GetCycleCount()//测量时间的计数器,返回的是计数值,不是绝对时间值.

{

__asm _emit 0x0F

__asm _emit 0x31

}

void test_time_funtion(void (* func)(pVT,pVT),pVT pBeg,pVT pEnd ,const string & s,SIZE_T N){
if(pEnd <= pBeg + 1){
cout<<"数据错误,停止测试"<<endl;
return ;
}

CTIME t = GetCycleCount();
const CTIME CPU_FREQUENCY = 2600000;//根据自己CPU频率自己调整
SIZE_T max = N;
for(max /= pEnd - pBeg;max ;max --)
func(pBeg,pEnd);
t = GetCycleCount() - t;
cout<<s<<" 花费时间"<<t / CPU_FREQUENCY<<"毫秒"<<endl;
}

void check(pVT pDest,pVT pBeg,pVT pEnd){
for(;pBeg < pEnd;){
if(* pBeg ++ != * pDest ++){
cout<<"算法不正确"<<endl;
return ;
}
}
cout<<"算法正确"<<endl;
}


void init(pVT pDest,pVT pBeg,pVT pEnd){
for(;pBeg < pEnd;)
* pDest ++ = * pBeg ++;
}




int _tmain(int argc, _TCHAR* argv[])
{
srand((unsigned int) time(NULL));
SIZE_T n = 100000000;
SIZE_T N = 100000000;
pVT v,v1,v2;
v = new VT[n];
v1 = new VT[n];
v2 = new VT[n];

for(SIZE_T x = 0; x < n; ++ x){
v[x] = rand() * 65536 + rand();//随机数据
//v[x] = (rand() * 65536 + rand()) % 1000;//大量重复数据
//v[x] = n - x;//逆序
//v[x] = x;//正序(即已经排序好的)
}
init(v1,v,v + n);
test_time_funtion(sort,v1,v1 + n,"标准库sort",N);

//init(v2,v,v + n);//100000
//test_time_funtion(insert_sort,v2,v2 + n,"insert_sort",N);
//check(v2,v1,v1 + n);

//init(v2,v,v + n);
//test_time_funtion(sort_kn_base,v2,v2 + n,"sort_kn_base",N);
//check(v2,v1,v1 + n);

//init(v2,v,v + n);
//test_time_funtion(sort_kn_base1,v2,v2 + n,"sort_kn_base1",N);
//check(v2,v1,v1 + n);

//init(v2,v,v + n);
//test_time_funtion(sort_kn_base2,v2,v2 + n,"sort_kn_base2",N);
//check(v2,v1,v1 + n);

//init(v2,v,v + n);
//test_time_funtion(sort_kn_base3,v2,v2 + n,"sort_kn_base3",N);
//check(v2,v1,v1 + n);

//init(v2,v,v + n);
//test_time_funtion(sort_kn_1,v2,v2 + n,"sort_kn_1",N);
//check(v2,v1,v1 + n);

init(v2,v,v + n);
test_time_funtion(sort_kn_2,v2,v2 + n,"sort_kn_2",N);
check(v2,v1,v1 + n);
delete v;
delete v1;
delete v2;
return 0;
}
...全文
216 13 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
knate 2010-06-26
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 yukuilongqq 的回复:]

用什么编译过的呀 我用dvc++ 怎么编译不过呀?
__asm _emit 0x0F

__asm _emit 0x31
有错呀
[/Quote]
不是无返回,而是返回值已经直接就存放在寄存器上.
这个嵌入了机器码.
详细的 搜索下关键字
__asm _emit 0x0F
估计google有很多很多详细的介绍.
如果出错的话,可能编译器不支持(CPU似乎是奔腾级以后就可以支持了,似乎现在CPU基本都支持),可以直接调用time 来测量时间.
没有本质上的区别.

PS:
顺带回复一下10楼:
这个不是有没有用的问题,而是直接就没有多少用处.
这些排序都是一些不成熟的方案.
离实际应用还有很远很远.
真的,很远很远.
我发这贴的最初目的是想找一些能对快速排序优化的方法.
YUKUILONGQQ 2010-06-24
  • 打赏
  • 举报
回复

inline unsigned __int64 GetCycleCount()//测量时间的计数器,返回的是计数值,不是绝对时间值.

{

__asm _emit 0x0F

__asm _emit 0x31

}


这什么意识呀 ??
__asm _emit 0x0F
__asm _emit 0x31
这两个是什么呀??
没参数 没return 怎么返回值呀?
xinzaiyiqi 2010-06-24
  • 打赏
  • 举报
回复
几种排序方法的用途都是在哪啊?
YUKUILONGQQ 2010-06-24
  • 打赏
  • 举报
回复
用什么编译过的呀 我用dvc++ 怎么编译不过呀?
__asm _emit 0x0F

__asm _emit 0x31
有错呀
knate 2010-06-22
  • 打赏
  • 举报
回复

//非递归方式最方便的就是直接模拟堆栈.
//实质上还是递归,只不过是自己维护堆栈,
void _sort_kn_4(pVT pBeg,SIZE_T n){
vector<pair<pVT,SIZE_T> > v;
v.resize(50);
v.clear();
v.push_back(pair<pVT,SIZE_T> (pBeg,n));
if(n < 2)
return ;
if(n < 32){
_insert_sort(pBeg,n);
return ;
}

for(;v.size();){
pVT pDest = (v.end() - 1)->first;
SIZE_T n = (v.end() - 1)->second;
v.pop_back();

if(n < 2)
continue;
if(n < 32){
_insert_sort(pDest,n);
continue;
}
SIZE_T i = __divide_min(pDest,n,find_rand(pDest,n));
if(i < n / 2){
v.push_back(pair<pVT,SIZE_T>(pDest,i));//缓存数量较小的部分
}
else{
v.push_back(pair<pVT,SIZE_T>(pDest + i + 1,n - i - 1));
}
if(i < n / 2){
SIZE_T t = find_rand(pDest + i + 1,n - i - 1);
if(*(pDest + i) == *(pDest + i + 1 + t))
t = __divide_equal(pDest + i + 1,n - i - 1,t);
else{
t = __divide_min(pDest + i + 1,n - i - 1,t);
v.push_back(pair<pVT,VT>(pDest + i + 1,t));
}
v.push_back(pair<pVT,VT>(pDest + i + 1 + t + 1,n - i - t - 1 - 1));
}
else{
SIZE_T t = __divide_min(pDest,i,find_rand(pDest,i));
v.push_back(pair<pVT,VT>(pDest,t));
v.push_back(pair<pVT,VT>(pDest + t + 1,i - t - 1));
}
}
}

ithiker 2010-06-21
  • 打赏
  • 举报
回复
汇编看不太懂
AAA20090987 2010-06-21
  • 打赏
  • 举报
回复
菜鸟来学习一下
knate 2010-06-21
  • 打赏
  • 举报
回复

//在基本快速排序里有个试验,在有大量重复元素时,进行划分出中值,这个是一个合理的方案,
//就这个思路加入剔除和中值相等的元素操作.
void _sort_kn_2(pVT pDest,SIZE_T n){
if(n < 2)
return ;
if(n < 32){
_insert_sort(pDest,n);
return ;
}
SIZE_T i = __divide_min(pDest,n,find_rand(pDest,n));
_sort_kn_2(pDest,i);
n -= i;
pDest += i;
if(n < 2)
return ;
if(n < 32){
_insert_sort(pDest,n);
return ;
}

i = find_rand(pDest,n);
if(*pDest == *(pDest + i))
//如果第二次随机值仍然相同,由于是随机值,两次都相同因此大致考虑为和中值相等的值较多,可以考虑剔除相等的.
//如果不同,则看成是对这部分进行了一次划分.
//这里可以注意的,这里随机选值包含了上一次划分剩下的那个中值.
//如果比较相等比较多花销,可以考虑把那个剔除,但程序稍微复杂点.
i = __divide_equal(pDest,n,0);
else{
i = __divide_min(pDest,n,i);
_sort_kn_2(pDest,i);
}
_sort_kn_2(pDest + i + 1,n - i - 1);

}
void sort_kn_2(pVT pBeg,pVT pEnd){
if(pBeg + 1 < pEnd)
_sort_kn_2(pBeg,pEnd - pBeg);
}
//晚上考虑递归深度问题.
knate 2010-06-21
  • 打赏
  • 举报
回复

//由于考虑如果直接所使用随机可能出现极坏的情况,因此在选取随机值时用若干个值的中值作为中值.
//我这里选择两个固定点,和一个随机位置.这三个的中值返回.
//我看过标准库,似乎里面选择的是9个(分成3组)固定位,这三组的中值再进行选取中值作为返回.
//这里简单起见,只用3个元素,感觉如果3个都用随机位置,开销大点,而且效果似乎差不多.
SIZE_T find_rand(pVT pDest,SIZE_T n){
SIZE_T n1 = n / 4,n2 = n - n/4,n3;
if(n < 256)
n3 = n / 2;
else
n3 = rand() % n;
if(*(pDest + n2) < *(pDest + n1))
swap_kn(*(pDest + n2),*(pDest + n1));
if(*(pDest + n3) < *(pDest + n2))
swap_kn(*(pDest + n3),*(pDest + n2));
if(*(pDest + n2) < *(pDest + n1))
swap_kn(*(pDest + n2),*(pDest + n1));
return n2;
}

void _sort_kn_1(pVT pDest,SIZE_T n){
if(n < 2)
return ;
if(n < 32){//这个考虑到递归层数可能太深,而且数据少时继续用随机排序花销比例就大了
//32是参考标准库的划分的
_insert_sort(pDest,n);
return ;
}
SIZE_T i = __divide_min(pDest,n,find_rand(pDest,n));
_sort_kn_1(pDest,i);
_sort_kn_1(pDest + i + 1,n - i - 1);
}
void sort_kn_1(pVT pBeg,pVT pEnd){
if(pBeg + 1 < pEnd)
_sort_kn_1(pBeg,pEnd - pBeg);
}
knate 2010-06-21
  • 打赏
  • 举报
回复
汇编的大概就是读取一个CPU内部还是哪里的一个计数器,好像叫什么时间戳什么的.似乎是硬件相关的.部分CPU似乎不支持.
反正是一个比较精确的计时器来的.可以达到指令级的

//由于sort_kn_base2里有可能出现中值是极大或极小值,从而退化到每次划分只能剔除中值一个值
//因此在这里增加一次划分,直接划分出和比较基准值相同的,看看效果.
void _(pVT pDest,SIZE_T n){
if(n < 2)
return ;
if(n < 32){
_insert_sort(pDest,n);
return ;
}
SIZE_T i = __divide_min(pDest,n,rand() % n);
_sort_kn_base3(pDest,i);
n -= i;
pDest += i;
i = __divide_equal(pDest,n,0);//上面的划分位于i的值必然是val.
_sort_kn_base3(pDest + i,n - i);
}
void sort_kn_base3(pVT pBeg,pVT pEnd){//从测试时3比2慢,可以基本可以看出随便剔除最小值,并不划算.
//注:选用大量重复数据时,效果不错,要比base2快.
//基本的快速排序我的想法基本结束,下面就是围绕如何合理选择随机值 和 插入剔除比较值相等的操作
if(pBeg + 1 < pEnd)
_sort_kn_base3(pBeg,pEnd - pBeg);
}

knate 2010-06-20
  • 打赏
  • 举报
回复
没什么意思.
只是把自己的一些笔记写上来尔尔.
好玩的话就看看吧.

sort_kn_base
的缺点是明显的,
由于每次划分都取第一个,并不能保证每次都能够划分差不多的大小的两部分.
因此,这次就不都去第一位,而是随机取其中的一个数,然后用他作为中值进行划分.
写成这样

void _sort_kn_base1(pVT pDest,SIZE_T n){
if(n < 2)
return ;

SIZE_T i = __divide_min(pDest,n,rand() % n);
_sort_kn_base1(pDest,i);
_sort_kn_base1(pDest + i + 1,n - i - 1);
}
void sort_kn_base1(pVT pBeg,pVT pEnd){
if(pBeg + 1 < pEnd)
_sort_kn_base1(pBeg,pEnd - pBeg);
}


超级大笨狼 2010-06-20
  • 打赏
  • 举报
回复
什么意思呢?
knate 2010-06-20
  • 打赏
  • 举报
回复

//////////////////////////////////////////////////////////////////////////排序
void _insert_sort(pVT pDest,SIZE_T n){
if(n < 2)
return ;
for(SIZE_T x = 1;x < n;++ x){
for(SIZE_T i = x;i;-- i){
if(pDest[i] < pDest[i - 1])
swap_kn(pDest[i],pDest[i - 1]);
else
break;
}
}
}

void insert_sort(pVT pBeg,pVT pEnd){
if(pBeg + 1 < pEnd){
_insert_sort(pBeg,pEnd - pBeg);
}
}
//插入排序
//插入排序是我觉得在初级排序算法里一个比较简单而且在大部分情况都相当高效的
//一个算法.特意保留了下来,而且后面要用到的.

//这个是根据指定的num_val位置的值,划分为小于和不小于两部分.
SIZE_T __divide_min(pVT pDest,SIZE_T n,SIZE_T num_val){
swap_kn(* pDest,*(pDest + num_val));
VT val = *pDest;
SIZE_T i = 0,j = n - 1;
for(;i < j;){
while(i < j && !(*(pDest + j) < val)) -- j;
*(pDest + i) = *(pDest + j);
while(i < j && *(pDest + i) < val) ++ i;
*(pDest + j) = *(pDest + i);
}
*(pDest + i) = val;
return i;
}
//这个类似,划分为相当和大于两部分.(调用前已经确定是最小值了)
//实际上num_val确定是0了,为了统一形式,没取消.
SIZE_T __divide_equal(pVT pDest,SIZE_T n,SIZE_T num_val){
//cout<<"调用相同"<<*(pDest + num_val)<<endl;
swap_kn(* pDest,*(pDest + num_val));
VT val = *pDest;
SIZE_T i = 0,j = n - 1;
for(;i < j;){
while(i < j && val < *(pDest + j)) -- j;
*(pDest + i) = *(pDest + j);
while(i < j && *(pDest + i) == val) ++ i;
*(pDest + j) = *(pDest + i);
}
*(pDest + i) = val;
return i;
}

//典型的基本的快速排序结构.
void _sort_kn_base(pVT pDest,SIZE_T n){
if(n < 2)
return;
SIZE_T i = __divide_min(pDest,n,0);
_sort_kn_base(pDest,i);
_sort_kn_base(pDest + i + 1,n - i - 1);
}
void sort_kn_base(pVT pBeg,pVT pEnd){
if(pBeg + 1 < pEnd)
_sort_kn_base(pBeg,pEnd - pBeg);
}


33,027

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧