以下确定性(无比较器)算法适用于输入元组:(a1,…,an)
- 不要在费雪耶茨洗牌使用比较有一些静态对(说)为抛硬币(做接受-拒绝抽样)。如果比较器第一次输出,则在确定性情况下将其反相使用,以免产生无限的拒绝环路。 1a1<a21
- (可选的加速方法:尝试对次,其中是长度或您的输入。如果任意两个输出不同,则返回在(1)中获得的排列)ñnn
- 使用合并排序对数组进行排序。
给定确定的顺序关系作为比较器,此算法会按时间对数组进行排序,因为Fisher-Yates随机播放使用最大在运行每个步骤和合并排序中的非随机“随机位”(例如,对比较器的调用)具有相同的渐近复杂度。在这种情况下,(1)的结果是完全没有用的,但是由于它后面跟着一个真正的排序,所以这没有害处。O(n )O(log n )O(nlogn)O(n)O(logn)
给定一个真正的硬币翻转,比较器(1)会对每个排列以相同的概率对数组进行排列,并且如果您确实必须执行(3)(忽略了(2)或(2)无法确定随机性),则为否损害是因为其结果的分布仅取决于其输入的顺序,由于(1),其输入在所有排列之间是均匀分布的,因此,整个算法的结果也是均匀分布的。每个接受-拒绝采样必须重复的次数是几何分布的(以概率拒绝),因此期望值。每次重复最多使用位,因此运行时分析与确定性情况几乎相同,但是我们只能得到一个 <2lognO(nlogn)<12<2logn预期运行时的,具有nontermination的可能性(终止仅几乎肯定)。O(nlogn)
正如Joe指出的那样:如果您不喜欢测试(1)中的第一位,请执行(3)然后执行(1)并使用始终为,因为数组已经在确定性情况下进行了排序。另外,您必须从循环范围的上限减去您的随机数,因为随机数的上限会产生相同的排列。但是请注意,此时禁止使用(2),因为在勒索赎金案中,您总是必须洗牌。an<a10
您甚至可以对(1)和(3)使用相同的调用比较器,但是如果可能的话,证明结果均匀分布至少要困难得多。
以下算法没有随机洗牌和排序的阶段,但是渐近变慢。它本质上是带有
二进制搜索的插入排序。我将使用表示输入,而表示第轮之后的结果:
a=(a1,…,an)bk=(bk,1,…,bk,k)k
- 设置b1,1=a1
- 如果则和否则和。无论哪种情况,对于非随机比较器,始终为(即false)。b 2 = (一个2,一个1)(ç ,d ):= (2 ,1 )b 2 = (一个1,一个2)(ç ,d ):= (1 ,2 )一个d < a c 0a2<a1b2=(a2,a1)(c,d):=(2,1)b2=(a1,a2)(c,d):=(1,2)ad<ac0
- 为了获得为获得第一。bkk≥3bk−1
- 令并且,即是的最小幂,且不小于。l=⌈log2k⌉k′=2lk′2k
- 令。对于每个让
i0=0j∈{1,…,l}
ij=⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪ij−1+2l−jij−1ij−1+2l−jij−1ij−1+2l−j>k−1∧ad<acij−1+2l−j>k−1∧¬(ad<ac)ij−1+2l−j≤k−1∧bk−1,ij−1+2l−j<akij−1+2l−j≤k−1∧¬(bk−1,ij−1+2l−j<ak)
- 如果重复(5.),否则il>kbk=(bk−1,1,…,bk−1,il−1,ak,bk−1,il,…,bk−1,k−1)
- 输出bn
随机情况:5 + 6的if子句实质上是接受-拒绝采样。该算法的其余部分是简单的混洗:混洗前个元素,并以相等的概率将第个元素添加到每个位置。如果使用正态插入排序,则会得到二项式分布。k−1k
请注意,与Fisher-Yates随机排序和合并排序相比,此算法在两种模式下均效率低下,因为如果使用数组,则将元素插入任意位置的开销很大,如果使用列表,则二进制搜索需要线性时间。但是也许以类似方式修改堆排序或树排序可能会导致算法更快。