如何有效*近乎*排序列表?

时间:2012-02-02 16:52:26

标签: algorithm sorting random

我有一个项目清单;我想对它们进行排序,但我想要一个随机性的小元素,因此它们不是严格按顺序排列,只是平均排序。

如何最有效地完成这项工作?

我不介意随机的质量是否特别好,例如它只是基于输入的机会排序,例如早期终止的不完整排序。

上下文通过引入一个非常微小的不精确元素来实现几乎贪婪的搜索;这是一个紧凑的循环,因此排序和调用random()的速度将被视为

我目前的代码是执行std::sort(这是C ++),然后在数组的早期部分进行一次非常简短的随机播放:

for(int i=0; i<3; i++) // I know I have more than 6 elements
    std::swap(order[i],order[i+rand()%3]);

8 个答案:

答案 0 :(得分:2)

使用JSort的前两次传球。构建堆两次,但不执行插入排序。如果随机元素不够小,请重复。


有一种方法(与不完整的JSort不同)允许对结果随机性进行更精细的控制,并且时间复杂度取决于随机性(需要的随机结果越多,时间复杂度越小)。将heapsort与Soft heap一起使用。有关软堆的详细说明,请参阅pdf 1pdf 2

答案 1 :(得分:1)

如果您确定该元素最多k远离它们应该的位置,则可以将快速排序N log(N)排序时间复杂度降低到N log(k) ....

修改

更具体地说,您将创建k个桶,每个桶包含N / k个元素。

您可以对每个存储桶进行快速排序,这需要k * log(k)次,然后对N/k个存储桶进行排序,这需要N/k log(N/k)次。将这两者相乘,您可以在N log(max(N/k,k))

中进行排序

这很有用,因为您可以并行运行每个存储桶的排序,从而减少总运行时间。

如果您确定列表中的任何元素在排序后距离其正确位置最多为k个索引,则此方法有效。

但我认为你没有任何限制。

答案 2 :(得分:1)

您可以使用标准排序算法(可用标准库吗?)并传递一个“知道”的谓词,给定两个元素,它们小于另一个元素,或者如果它们相等(返回-1,0或1)。在谓词中,然后引入一个罕见的(可配置的)案例,答案是随机的,使用随机数:

伪代码:

if random(1000) == 0 then
  return = random(2)-1   <-- -1,0,-1 randomly choosen

这里我们有1/1000机会“赌博”两个元素,但这个数字严格取决于要分类的容器大小。

在1000个案例中添加的另一件事可能是删除“正确”的答案,因为这不会扰乱结果!

编辑:

if random(100 * container_size) == 0 then <-- here I consider the container size
{
   if element_1 < element_2
      return random(1); <-- do not return the "correct" value of -1
   else if element_1 > element_2
      return random(1)-1; <-- do not return the "correct" value of 1
   else
      return random(1)==0 ? -1  : 1; <-- do not return 0
}

在我的伪代码中: random(x)= y其中0 <= y <= x

答案 3 :(得分:1)

一种可能需要更多空间但可以保证现有排序算法无需修改即可使用的可能性是创建排序值的副本,然后在排序之前以某种方式修改它们(然后使用修改后的值()。

例如,如果要排序的数据是简单字符字段Name[N],则添加一个名为NameMod[N]的字段(假设数据在结构或类中)。使用NameMod的副本填写Name,但添加一些随机化。然后3%的时间(或一些适当的数量)改变名称的第一个字符(例如,将其改变+/-一个或两个字符)。然后10%的时间改变第二个字符+/-几个字符。

然后通过您喜欢的任何排序算法运行它。好处是您可以轻松地更改这些百分比和随机性。并且排序算法仍然有效(例如,它不会有比较函数返回不一致结果的问题)。

答案 4 :(得分:1)

将列表拆分为两个大小相同的部分。使用任何常用算法分别对每个部分进行排序。然后合并这些部分。像往常一样执行一些合并迭代,比较合并元素。对于其他合并迭代,不要比较元素,而是从同一部分中选择元素,如上一步骤所示。没有必要使用RNG来决定如何处理每个元素。只需忽略每个第N个元素的排序顺序。

此方法的其他变体几乎数组几乎就地。使用奇数/偶数索引将数组拆分为两部分。排序他们。 (甚至可以使用标准C ++算法和适当修改的迭代器,如boost :: permutation_iterator)。在阵列的末尾保留一些有限的空间。合并零件,从最后开始。如果合并的部分将覆盖其中一个非合并元素,则只需选择此元素即可。否则按排序顺序选择元素。随机性级别由预留空间量决定。

答案 5 :(得分:1)

假设您希望数组按升序排序,我会执行以下操作:

for M iterations
  pick a random index i
  pick a random index k
  if (i<k)!=(array[i]<array[k]) then swap(array[i],array[k])

M控制数组的“排序” - 随着M增加,数组变得越来越有序。我会说M的合理值是n ^ 2,其中n是数组的长度。如果选择随机元素太慢,那么您可以事先预先计算其索引。如果方法仍然太慢,那么你可以总是以减少排序为代价来减少M.

答案 6 :(得分:0)

Bubblesort救援!

对于未排序的数组,您可以选择一些随机元素并向上或向下冒泡。 (也许通过旋转,效率更高一些)很难控制(dis)顺序的数量,即使你选择所有N个元素,你也不确定整个数组是否会被排序,因为元素被移动了并且您无法确保仅触摸每个元素一次。

顺便说一句:这种问题往往发生在游戏引擎中,其中候选移动的列表保持或多或少的排序(因为加权采样),并且每次迭代后的排序太昂贵,并且只有一个或者预计会有一些因素发生变化。

答案 7 :(得分:0)

获取一小部分随机数据并对其进行排序。您可以将其用作映射,以估计每个元素在最终几乎排序的列表中应出现的位置。您现在可以扫描完整列表并移动/交换位置不佳的元素。

这基本上是O(n),假设子集的小的初始排序不需要很长时间。希望您可以构建地图,以便快速提取估算值。