【问题标题】:What's the efficiency and quality of this shuffling algorithm?这种洗牌算法的效率和质量如何?
【发布时间】:2010-09-27 09:20:03
【问题描述】:

最近这个关于sorting randomly using C# 的问题让我想到了我有时在 Perl 中洗牌的方式。

@shuffled = sort { rand() <=> rand() } @array;

上述问题中建议的解决方案是Fisher-Yates shuffle,它在线性时间内起作用。

问题是:我的 sn-p 效率如何?这种随机播放“真的”是随机的吗?

【问题讨论】:

  • 感谢大家确认我自己也怀疑过的事情;它的排序很糟糕,而且它不是正确的洗牌数组的方法。我从来没有在关键的地方使用过它(无论如何,很少洗牌是关键的),但作为一种快速的测试方法。但它仍然以某种扭曲的方式优雅;)
  • brian 提出的新标题改变了讨论的性质和目的,将其回滚。
  • brian 更改了标题,因为这是关于 Perl 邮件列表和 IRC 上的#perl 的一个非常常见的问题,因此经常被问到 perlfaq4 包含答案。

标签: perl sorting random performance


【解决方案1】:

我不是 Perl 内部专家,所以我不知道“排序”在这里如何工作。但是,大多数排序函数都希望它们的比较具有一致性,如果函数本身是随机的,我希望它们会以不可预测的方式工作。不幸的是,不可预测性与随机性不同,所以我对你的洗牌数组没有信心。它可能倾向于将元素按某种顺序排列,就像仓促创建的复杂递归关系可能不是随机的一样。

我建议不要分析排序函数,而是使用 Fisher-Yates。

正如 Knuth 所说,随机性太重要了,不能任凭运气。

【讨论】:

    【解决方案2】:
    $ perldoc List::Util
    ⋮
      shuffle LIST
           Returns the elements of LIST in a random order
    
               @cards = shuffle 0..51      # 0..51 in a random order
    ⋮
    

    这是我的建议。

    【讨论】:

      【解决方案3】:

      您提议的 shuffle 有效,我实际上有点惊讶。在 Perl sort 函数的实现中,它尝试根据比较函数的值将数组的元素按升序排列。问题是,您的比较函数不会返回一致的答案!有时它可能会说"foo" lt "bar",而其他时候它可能会说"bar" lt "foo"。这有可能将排序算法混淆到它永远不会终止、或因致命错误或其他一些灾难性故障而终止的地步。

      【讨论】:

        【解决方案4】:

        sort 上的 perl 文档说明了这一点

        比较函数是必须的。如果它返回不一致的结果(例如,有时说 $x[1] 小于 $x[2],有时说相反),则结果定义不明确。

        所以这样做是个坏主意。

        ETA:我刚刚做了一个基准测试。在 100000 个元素的数组上,使用 FY-shuffle 也快 10 倍以上。

        【讨论】:

          【解决方案5】:

          一方面,您知道无论您使用什么比较器,sort() 都不可能比 O(n log n) 快。所以即使它执行的 shuffle 是公平的,它的性能也会更差。

          洗牌公平吗?对于某些(易于分析的)排序算法来说,这显然是不公平的。考虑一个简单的冒泡排序——为了让一个元素从一端移动到另一端,比较函数必须对 n 次连续调用评估为正值——对于应该是 n 中的 1 事件的概率为 2 ^ n 中的 1。对于快速排序,很难分析,并且最终可能是公平的。但是,如果重要的是正确,那就以正确的方式去做。

          【讨论】:

            【解决方案6】:

            这只是直觉,但我认为使用这样的排序会产生一个集合,其顺序在某种程度上取决于原始集合的顺序。真正随机排序的结果根本不应该依赖于原始集合的顺序。我无法解释为什么/如何,也许其他人可以(或表明它实际上是随机的)?

            至于它的效率如何,我不确定,但它可能不会比使用sort 的任何其他类型的效率低很多,因为AFAIK rand() 相对便宜。不过,我可能错了。

            【讨论】:

            • 我收集的和你一样多,但它们也只是预感。我希望有人会,至少假装知道:)
            • @Tim:我不认为 rand() rand() 是一个洗牌,它不是一个正确的算法。它的结果是不可预测的,但不是随机的。除此之外,您正在重申我所说的。如果您使用的是真正的随机排序(随机排序),则初始排序无关紧要。
            【解决方案7】:

            有一个更好的 Fisher-Yates 洗牌函数,它不使用 perlfaq4: How do I shuffle an array randomly? 中的 sort 内置函数。

            【讨论】:

            • 感谢您指出 Perl 的精彩文档。我可能会补充一点,命令行中的 perldoc -q shuffle 是另一种查找相同信息的方法。
            【解决方案8】:
            
            @shuffled = map {
              $_->[1]
            } sort {
              $a->[0] <=> $b->[0]
            } map {
              [ rand(), $_ ]
            } @array;
            

            【讨论】:

              猜你喜欢
              • 2010-12-30
              • 1970-01-01
              • 1970-01-01
              • 2016-06-27
              • 2014-12-22
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多