4
这种混洗算法有什么问题(如果有的话),我怎么知道?
就像背景一样,我知道Fisher-Yates完美的随机播放。它的O(n)复杂度和保证的均匀性是一个很好的改组,如果在允许就地更新数组的环境中使用它,我真是个傻瓜(所以在大多数情况下,如果不是全部,命令式编程环境)。 遗憾的是,函数式编程世界无法让您访问可变状态。 但是,由于Fisher-Yates的缘故,我没有太多关于如何设计改组算法的文献。在解决这个问题的几个地方都做了简短的说明,然后才说“实际上是Fisher-Yates,这是您需要知道的所有内容”。最后,我必须提出自己的解决方案。 我想出的解决方案是这样的,它可以随机排列任何数据列表: 如果列表为空,则返回空集。 如果列表中有单个项目,则返回该单个项目。 如果列表非空,请使用随机数生成器对列表进行分区,然后将算法递归应用于每个分区,以组合结果。 在Erlang代码中,它看起来像这样: shuffle([]) -> []; shuffle([L]) -> [L]; shuffle(L) -> {Left, Right} = lists:partition(fun(_) -> random:uniform() < 0.5 end, L), shuffle(Left) ++ shuffle(Right). (如果对您来说,这看起来像是一种疯狂的快速排序,那么基本上就是这样。) 因此,这就是我的问题:使得费舍尔-耶茨(Fisher-Yates)很难找到改组算法的情况使寻找工具来分析改组算法同样困难。在分析PRNG的均匀性,周期性等方面,我可以找到很多文献,但是关于如何分析混洗的信息却很少。(实际上,我发现的有关分析混洗的一些信息完全是错误的,很容易通过简单的技术来欺骗。) 所以我的问题是:我该如何分析我的改组算法(假设random:uniform()调用是由生成具有良好特征的适当随机数的任务决定的)?我可以使用哪些数学工具来判断,例如,在1..100范围内的整数范围内的100,000次混洗运行是否给了我很好的混洗结果?我已经进行了一些测试(例如,将随机播放中的增量与减量进行比较),但是我想知道更多一些内容。 并且,如果对这种随机播放算法本身有任何见解,也将不胜感激。