切换到宽版
  • 21318阅读
  • 26回复

[原创]快排改进算法 [复制链接]

上一主题 下一主题
离线大壮
 
只看楼主 倒序阅读 0 发表于: 2007-03-11
最近研究快排,对快排对低熵数据的效率很不满意,于是研究了对快排平均效率的提高的问题。

快排处理有序数据时,仍需要进行划分,一分到底,尤其是大部分数据集中在一个集合中,使快排退化为了冒泡。传统做法是在数据中任取一数作为划分轴,该法确实大大提高了快排的平均效率,但是这样仍不能达到对于已排序数据O(n)的时间效率。那么,还有什么办法改进呢?

我们发现,主要问题集中在传统快排无法对数据整体进行判断,所有情况都按照熵最高处理,那么,只要在快排中加入数据分析,就可以避免遇到低熵数据仍进行大量运算。

那么,我们在划分函数中,在high与low移动至中间时,比较相邻两个数据大小,并按照顺序进行交换,类似于冒泡法,并定义两个变量,对low与high是否交换过进行计数。若没有交换过,则表明该部分数据已经有序,在qsort函数递归时,不必再对low或high进行操作,即不必对low或high进行递归。这样,就大大减少了递归次数,降低了时间复杂度。如此一来,传统情况下的最差情况,似乎就成了最好情况,由O(n^2)变成了O(n)。

c版源代码:
void swap(int *px, int *py){
//定义交换函数
int temp;

temp = *px;
*px = *py;
*py = temp;
}

int partition(int data[], int low, int high, int *bol, int *boh){
//定义划分函数
if(data[low] >= data[high]){
if (data[low] > data[(low + high) / 2]) swap(&data[low], &data[(low + high) / 2]);
else if(data[high] > data[(low + high) / 2]) swap(&data[low], &data[high]);
}
else{
if(data[high] < data[(low + high) / 2]) swap(&data[low], &data[high]);
else if(data[low] < data [(low + high) / 2]) swap(&data[low], &data[(low + high) /2]);
}//对data[low],data[high],data[(low + high) / 2]取中值与data[low]交换
//此处不推荐使用时间随机函数,因为好的随机函数本身有很大的时间复杂度

int key = data[low], countl = 0, counth = 0, n = high, m = low;//countl,counth分别对low子表与high子表数据交换情况计数
int *temh = boh, *teml = bol;
while(low < high){
while(low < high && data[high] >= key){
  if(high < n)
  if(data[high] > data[high + 1]){
  //进行冒泡操作
  swap(&data[high], &data[high + 1]);
  //对交换操作进行计数
  counth = 1;
  }
  --high;//移动指针
}
data[low] = data[high];//交换高低指针数据

while(low < high && data[low] <= key){
  if(low > m)
  if(data[low - 1] > data[low]){
  //进行冒泡操作
  swap(&data[low - 1], &data[low]);
  //对交换操作进行计数
  countl = 1;
  }
  ++low;//移动指针
}
data[high] = data[low];//交换高低指针数据

}
data[low] = key;
*temh = counth;
*teml = countl;
return low;
}

void qsort(int data[], int low, int high){

//定义递归实现qsort函数
if(low == high) return;//递归终点
else{
int bol,boh;

int key = partition(data, low, high, &bol, &boh);
if(bol ^ 0) qsort(data, low, key - 1);//根据标记判断是否对low子表进行操作
if(boh ^ 0) qsort(data, key + 1, high);//根据标记判断是否对high子表进行操作
return;
}
}

好不容易调出来了,因为疏忽,把l和h搞反了,调了两晚上才弄出来,无语……再也不犯低级错误了!·¥#%!

在ms vs 2003 vc++下调试通过,时间问题没有与改进前算法比较效率,有兴趣的测试一下吧
离线edwin.k1
只看该作者 1 发表于: 2007-03-15
有侔PASCAL?
离线stevenjl

只看该作者 2 发表于: 2007-03-15
个人推荐希尔排序
Dream Walker...
离线swj05652
只看该作者 3 发表于: 2007-03-16
没有很看明白,楼主能否不用熵的概念解释一下?
离线大壮
只看该作者 4 发表于: 2007-03-17
希尔的效率比快排要低,实现也不比快排简单,干吗要用希尔?
其实用不用熵解释都一样啦,把高熵理解为混乱无序的,把低墒理解成基本有序的就可以了。
离线stevenjl

只看该作者 5 发表于: 2007-03-17
希尔的效率比快排要低,实现也不比快排简单,干吗要用希尔?


错了吧……希尔排序当然比快排简单,小数据的效率也要超过快排,退化情况也不严重
Dream Walker...
离线noipmar
只看该作者 6 发表于: 2007-03-18
哦?...........................
离线stevenjl

只看该作者 7 发表于: 2007-03-18
一、测试系统

CPU: Intel Celeron 4 2.0 GHz Memory: 256MB

System: Windows XP SP2 \ Cena 0.6

 

二、待测试算法(均用C语言编写)

1.快速排序(无优化,追求程序简单)【Qsort

2.随机化快速排序【Rnd_Qsort

3.楼主提供的程序【DZ_Qsort

4.希尔排序【Shell

 

三、数据

测试点

长度

备注

1

1024

随机数据(随机范围0~maxint,下同)

2

10240

随机数据

3

102400

随机数据

4

102400

随机数据

5

1024000

随机数据

6

2000000

递增数据(1,2,3…2000000)

7

2000000

递减数据(2000000…3,2,1)

8

2000000

峰状数据(1,2,3…1000000…3,2,1)

9

2000000

相同数据(32767…)

10

2000000

状数据(1000000…3,2,1,1,2,3…1000000)

 

四、文件长度(已经删除所有注释和段首空格)

程序

长度

Qsort

286字符

Rnd_Qsort

359字符

DZ_Qsort

1184字符

Shell

200字符

 

五、测试成绩

测试点

Qsort

Rnd_Sort

DZ_Qsort

Shell

1

0.01

0.01

0.01

0.03

2

0.03

0.03

0.01

0.03

3

0.26

0.25

0.23

0.29

4

0.25

0.25

0.26

0.26

5

2.39

2.32

2.39

3.31

6

60.06(超时中止)

5.09

4.85

5.2

7

60.08(超时中止)

5.17

4.9

5.25

8

60.13(超时中止)

5.07

4.84

5.18

9

60.05(超时中止)

4.56

4.29

4.6

10

60.03(超时中止)

5.06

39.03s后崩溃

4.85

 

注:测试方法与数据参考WasltoneOIBH的相关帖子

Dream Walker...
离线stevenjl

只看该作者 8 发表于: 2007-03-18
附上Shell排序PASCAL和C的代码:

PASCAL语言:
  1. procedure shell(max:longint);
  2. var
  3. i,j,x,d:longint;
  4. begin
  5. d:=max div 2;
  6. while d>=1 do
  7. begin
  8.   for i:=d+1 to max do
  9.   begin
  10.     x:=work[i];
  11.     j:=i-d;
  12.     while (j>0) and (x<work[j]) do
  13.     begin
  14.     work[j+d]:=work[j];
  15.     dec(j,d);
  16.     end;
  17.     work[j+d]:=x;
  18.   end;
  19.   d:=d div 2;
  20. end;
  21. end;


C语言:
  1. void shell(int *work,int n)
  2. {
  3. int i,j,x,d;
  4. d= n / 2;
  5. while (d>=1)
  6. {
  7.   for (i=d+1;i<=n;i++)
  8.   {
  9.     x=work[i];
  10.     j=i-d;
  11.     while ((j>0) && (x<work[j]))
  12.     {
  13.     work[j+d]=work[j];
  14.     j-=d;
  15.     }
  16.     work[j+d]=x;
  17.   }
  18.   d /= 2;
  19. }
  20. }
Dream Walker...
离线stevenjl

只看该作者 9 发表于: 2007-03-18
当然,楼主对快排的提速还是非常成功的。
但是对于谷状数据,看来还是有一些问题
Dream Walker...
快速回复
限100 字节
 
上一个 下一个