病理分选


15

病理分选

您的老板要求您开发一种排序算法,以提高公司应用程序的性能。但是,编写该应用程序后,您知道您不可能使其速度大大提高。不想让您的老板失望,您决定开发一种新算法,该算法对某些数据集的效果甚至比* sort好。当然,您不能使该算法仅在某些情况下起作用是显而易见的,因此您希望使其模糊。

竞赛的目的是用您选择的语言编写一个排序例程,该例程在某些数据集上的表现要优于其他数据集,并具有可重复的结果。确定速度的分类越具体越好。该算法必须进行某种排序,因此依赖于已完全排序的数据的算法(例如,什么都不做的算法)或依赖于已完全反向排序的数据的算法都是无效的。排序算法必须正确地对任何数据集进行排序。

介绍完例程后,请说明为什么它仅适用于某些数据集,并包括对至少一组良好(快速)数据和一组不良(缓慢)数据的测试。这里的目的是要向老板证明,您偶然发现了一种更好的排序方式,因此,更多的测试数据会更好。当然,您只会向老板展示来自良好数据的测试结果,因此所需测试数据中的缺陷不会太明显。如果适用于您的语言,请说明您的算法比语言的内置排序算法更快。

例如,可能会提交一种插入排序算法,其中好数据是已经接近排序的数据,坏数据是完全随机的数据,因为插入排序对接近排序的数据接近O(n)。但是,这不是很好,因为我的老板可能会注意到所有测试数据几乎都是从头开始排序的。

这是一场,因此7天(5月21日)后获得最高票数的答案将获胜。

如果没有人能击败我,我想提交一个利用统一分布的数据集的社区Wiki答案。


:对于那些接近这个问题可能是有用/有趣的资源“心灵排序算法” (声明:该文章的作者和我都非常接近:-P)
HostileFork表示不信任SE

Answers:


9

这已经是相当长的时间了,但是我记得在算法101中,我们曾经教过一些使用随机化的排序算法。我当时不是一个很好的学生,所以我真的不记得它的进展情况或为什么它平均起来很快。

尽管如此,我认为这个问题需要使用随机化的解决方案,希望该方法平均可以对我有利。

import random

def arrayIsSorted (arr) :
    for i in range(len(arr)-1) :
        if arr[i]>arr[i+1] : return False
    return True

def rSort (arr) :
    random.seed (42)
    counter = 0
    while not arrayIsSorted(arr) :
        random.shuffle (arr)
        counter+=1
    print ("Sorted in %d iterations." % counter)
    return arr

由于真正的随机性很重要,因此我确保为RNG注入生命,宇宙和一切的答案。经过一些测试,结果证明这是明智之举!查看这2个完全任意的列表排序的速度:

rSort ([6,1,4,2,3,7,5])
rSort ([8,9,6,1,4,7,2,3,5])

这两个函数仅在1次迭代中排序-您不可能要求比此更快的函数!

现在,诚然,其他一些列表产生的结果稍差一些……

rSort ([5,1,4,2,3,7,6])
rSort ([8,9,6,1,4,7,2,5,3])

这些分别以4,176和94,523次迭代进行排序,实际上需要花费一秒钟以上的时间……但是让我们把这个事实掌握在我们自己的头上,以免使任何人分心此算法的惊人之处!

编辑:

我被要求在100个项目的清单上证明我的算法的效率,因此您可以:

rSort ([70, 6, 52, 97, 85, 61, 62, 48, 30, 3, 11, 88, 39, 91, 98, 8, 54, 92, 44, 65, 69, 21, 58, 41, 60, 76, 27, 82, 93, 81, 20, 94, 22, 29, 49, 95, 40, 19, 55, 42, 43, 1, 0, 67, 35, 15, 51, 31, 16, 25, 5, 53, 37, 74, 86, 12, 13, 72, 56, 32, 47, 46, 59, 33, 80, 4, 45, 63, 57, 89, 7, 77, 14, 10, 34, 87, 18, 79, 9, 66, 24, 99, 64, 26, 78, 38, 90, 28, 83, 75, 68, 2, 17, 73, 96, 71, 23, 84, 36, 50])

即使是这个漫长而完全任意的列表,也会立即被排序!确实,我一定偶然发现了世界上最好的排序算法!


3
我们可以在稍大的数据集上获得一些测试结果吗?也许有100个元素?;)
Geobits,2014年

@Geobits没问题,这是:)
Tal

1
@Geobits是的。最终。
2014年

3
这很费力,但是可以说它使用了bogosort,如果有足够的时间,它将最终对数组进行排序。我愿意打赌,“洗牌和重复”可算是分类,尽管分类不好。
Millinon 2014年

1
如果这是真的随机混洗,也许。PRNG有一个循环,所以我看不到如何保证尝试所有排列。
Geobits

2

如果您可以创建自己的数据,那将非常简单-获取看起来随机的数据,但其中包含用于快速排序的键。所有其他数据均使用原始排序方法,因此平均时间更好。

一种简单的方法是确保每个数据项都有唯一的密钥,然后仅对密钥进行哈希处理。例如,使用一个列表,其中一个数字是1-10,000,所有数字都乘以16,并添加了一个0-15的随机数(请参见下面的fillArray())。它们看起来是随机的,但是每个都有唯一的顺序密钥。为了进行排序,将其除以16(在C中>> 4是非常快的),然后只需将数字使用结果键作为索引放置到数组中即可。一遍,您就完成了。在测试中,我发现Quicksort的速度比一千万个数字慢30倍。

void fillArray(int *a,int len)
{
  for (int i=0;i<len;++i)
    a[i]=(i<<4)|(rand()&0xF);
  // shuffle later
}
void sortArray(int *a,int len)
{
  int key=0;
  int *r=new int[len];
  for (int i=0;i<len;++i)
  {
    key=a[i]>>4;
    r[key]=a[i];
  }
  memcpy(a,r,len*sizeof(int));
  delete[] r;
}
void shuffleArray(int *a,int len)
{
  int swap=0, k=0;
  for (int i=0;i<len;++i)
  {
    k=rand()%len;
    swap=a[k];
    a[k]=a[i];
    a[i]=swap;
  }
}
int qCompare(const void*a,const void*b)
{
  int result=*((int*)a)-*((int*)b);
  return result;
}
void main()
{
  int aLen=10000;
  int *a=new int[aLen];
  srand (time(NULL));
  fillArray(a,aLen);
  // time them
  long t0=0, d0=0, d1=0;
  // qsort
  shuffleArray(a,aLen);
  t0=::GetTickCount();
  qsort(a,aLen,sizeof(int),&qCompare);
  d0=::GetTickCount()-t0;
  // oursort
  shuffleArray(a,aLen);
  t0=::GetTickCount();
  sortArray(a,aLen);
  d1=::GetTickCount()-t0;
  delete[] a;
}

任何具有唯一键的东西都可以通过这种方式进行排序-当然,如果您有存储它的内存。例如,许多数据库使用唯一的数字客户ID-如果列表足够小/顺序足够,则可以将其保存在内存中。或将记录转换为唯一编号的其他方法。有关更多信息,请研究哈希排序,因为这就是...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.