73
|
JUST MY correct OPINION · 技术社区 · 14 年前 |
![]() |
1
73
总评论我个人使用算法的概率正确性方法:如果你知道如何 证明 它是正确的,那么它可能是正确的;如果你不这样做,它肯定是错误的。 换言之,试图分析你能想到的每一种算法通常是没有希望的:你必须不断地寻找一种算法,直到你找到一种 可以 证明是正确的。 通过计算分布分析随机算法我知道有一种方法可以“自动”分析一个比简单的“抛出大量测试并检查一致性”更强大的混乱(或者更普遍地说是一个随机使用的算法)。您可以机械地计算与算法的每个输入相关联的分布。
一般的想法是,随机使用的算法探索一个可能世界的一部分。每次您的算法在集合中请求随机元素时({
一般来说,这需要对算法进行深度重写。如果您的语言支持带分隔符的延续,则不必这样做;您可以在请求随机元素的函数内部实现“对所有可能结果的探索”(其思想是,随机生成器不返回结果,而是捕获与程序相关联的延续,并使用所有不同的结果运行它)。有关此方法的示例,请参见Oleg's HANSEI . 一个中介,可能不那么神秘,解决方案是将这个“可能结果的世界”表示为一个monad,并使用诸如haskell这样的语言以及用于一个monadic编程的工具。下面是在Haskell中使用 probability 包裹:
您可以针对给定的输入运行它,并获得输出分布:
您可以看到这个算法对于大小为2的输入是一致的,但是对于大小为3的输入是不一致的。
与基于测试的方法的不同之处在于,我们可以在有限的步骤中获得绝对的确定性:它可以非常大,因为它相当于对可能世界的详尽探索(但一般小于2^n,因为存在类似结果的因式分解),但是如果它返回一个不均匀的分布,我们肯定地知道算法是错误的。当然,如果它返回
:由于特定的透视处理,此算法是Erlang实现的变体。如果我不使用Pivot,就像在您的例子中一样,输入的大小在每一步都不会减少:算法还考虑了所有输入都在左列表(或右列表)中的情况,并在无限循环中丢失。这是概率monad实现的一个弱点(如果一个算法的概率为0,则分布计算可能仍然存在分歧),我还不知道如何解决。 基于排序的无序播放下面是一个简单的算法,我确信我可以证明是正确的:
如果您知道碰撞的概率(两个随机选取的数字相等)足够低,则可以省略步骤2,但没有它,无序移动就不完全均匀。 如果您在[1..n]中选择您的密钥,其中n是集合的长度,则会发生很多冲突。( Birthday problem )如果您将密钥选为32位整数,在实践中冲突的可能性很低,但仍会受到生日问题的影响。 如果使用无限(延迟计算)位串作为键,而不是有限长度的键,则冲突的概率将变为0,并且不再需要检查是否清晰。 以下是OCAML中的无序处理实现,使用懒惰实数作为无限位串:
还有其他方法可以“纯粹的洗牌”。一个好的是阿普非默斯的 mergesort-based solution . 算法考虑:前面的算法的复杂性取决于所有键都是不同的概率。如果将它们选为32位整数,则某个键与另一个键碰撞的概率为40亿分之一。按这些键排序是O(n log n),假设选择的随机数是O(1)。 如果是无限位串,则不必重新开始选取,但复杂度与“平均计算流中有多少个元素”有关。我推测它是O(log n)的平均值(因此仍然是O(n log n),但没有证据。 …我认为你的算法有效在更多的反射之后,我认为(像douplep),您的实现是正确的。这是一个非正式的解释。
列表中的每个元素都是
测试
由几个
因此,您的算法相当于使用无限位串键进行排序。对列表进行分区的操作,类似于QuickSort对Pivot元素的分区,实际上是一种将具有值的元素(对于位串中的给定位置)进行分隔的方法。
排序是统一的,因为位串都是不同的。实际上,实数等于
概率终止关于您的算法(或我等价的基于排序的方法),一个微妙的点是终止条件是 概率的 . Fisher Yates总是在已知的步骤数(数组中的元素数)之后终止。使用您的算法,终止取决于随机数生成器的输出。
有可能的输出会使您的算法
偏离
,不终止。例如,如果随机数生成器总是输出
然而,如果你确信你的随机数生成器是公平的,这不是一个问题:它不会作弊,并且总是返回独立的均匀分布的结果。在这种情况下,测试
:有关数学详细信息,请参见 http://en.wikipedia.org/wiki/Measure_(mathematics)#Measures_of_infinite_intersections_of_measurable_sets 更一般地说,只有当一些元素与同一布尔流相关联时,算法才会终止。这意味着至少两个元素具有相同的布尔流。但是两个随机布尔流相等的概率又是0:K位置的数字相等的概率是1/2,所以n第一个数字相等的概率是2^-n,同样的分析也适用。 因此,你知道你的算法 以概率1终止 . 这是Fisher-Yates算法的一个较弱的保证, 总是终止 . 尤其是,你很容易受到一个邪恶的对手的攻击,这将控制你的随机数生成器。 有了更多的概率理论,您还可以计算给定输入长度下算法的运行时间分布。这超出了我的技术能力,但我认为这是好的:我认为您只需要查看O(logn)的第一位数字的平均值就可以检查所有n个惰性流是否都不同,并且更高运行时间的概率以指数形式减少。 |
![]() |
2
21
正如维基百科文章中所讨论的,您的算法是基于排序的无序排列。 一般来说,基于排序的无序处理的计算复杂性与底层排序算法(例如O.( n 日志 n 平均值,O( n )最坏的情况是基于快速排序的随机播放),而分布不是 完美 统一,它应该接近统一足够接近最实际的目的。 Oleg Kiselyov提供了以下文章/讨论: 它更详细地说明了基于排序的无序处理的局限性,并提供了两种对费希尔战略的调整:一种是简单的( n )一种是基于二叉树的( n 日志 n 一)。
这不是真的:当纯函数编程避免 副作用 它支持访问具有一流效果的可变状态,而不需要副作用。 在这种情况下,可以使用haskell的可变数组来实现本教程中描述的可变Fischeryates算法:
补遗洗牌排序的特定基础实际上是无限的密钥。 radix sort :正如Gasche指出的,每个分区对应一个数字分组。 其主要缺点与任何其他无限键排序混乱相同:没有终止保证。虽然终止的可能性随着比较的进行而增加,但从来没有上限:最坏情况下的复杂性是o()。 |
![]() |
3
3
不久前我做了一些类似的事情,特别是你可能对Clojure的向量感兴趣,它是功能性的和不变的,但仍然具有O(1)随机访问/更新特性。这两个gist有“从这个m-sized列表中随机抽取n个元素”的几个实现;如果允许n=m,其中至少有一个会变成fisher-yates的函数实现。 |
![]() |
4
1
基于 How to test randomness (case in point - Shuffling) 我建议: 随机播放(中等大小)由相等数量的零和一组成的数组。重复并连接,直到无聊为止。使用这些作为Diehard测试的输入。如果你有一个好的无序排列,那么你应该生成一个零和一的随机序列(注意,在中等大小的数组的边界处,零(或一)的累积过量是零,你希望测试能够检测到,但是越大的“中等”越不可能这样做)。 请注意,测试可以拒绝洗牌有三个原因:
如果任何测试被拒绝,您必须解决这种情况。 不同的适应 diehard tests (为了解决某些数字,我使用 source 从 diehard page )自适应的主要机制是使随机位随机乱序算法成为均匀分布的随机位源。
等等… |
![]() |
CXB · 洗牌-无重复JAVA 7 年前 |
![]() |
Eltorrooo · 为什么哈希集中的顺序从未改变?[副本] 7 年前 |
![]() |
Muiter · Foreach循环在输出时被洗牌[重复] 7 年前 |
![]() |
Sparrky · 我如何从无序函数中获取输出并围绕DOM元素移动? 7 年前 |
![]() |
Paul · 如何在php shuffle中组合两个数组 7 年前 |
![]() |
Sergio Manchado · 在两个列表之间随机移动元素 7 年前 |
![]() |
Joe · 无序排列内爆数组(HTML复选框到数组)[重复] 7 年前 |
![]() |
Roald · 围绕NA值对向量的部分顺序重新排序 7 年前 |