最近关于sorting randomly using C#的问题让我想到了我有时在Perl中改组数组的方式。
@shuffled = sort { rand() <=> rand() } @array;
上述问题中提出的解决方案是Fisher-Yates shuffle,它在线性时间内起作用。
问题是:我的代码片段有多高效,并且是“真正”随机的混乱?
答案 0 :(得分:9)
我不是Perl的内部专家,所以我不知道“sort”在这里会起作用。但是,大多数排序函数都希望它们的比较具有一致性,如果函数本身是随机的,我希望它们能够无法预测地工作。不幸的是,不可预测性与随机性不一样,所以我对你的洗牌阵列没有信心。它可能倾向于以某种顺序放置元素,就像匆忙创建和复杂的递归关系可能不是随机的一样。
我建议不要分析排序函数,而是选择Fisher-Yates。
正如Knuth所说,随机性太重要了,不值得冒险。
答案 1 :(得分:9)
$ perldoc List::Util
⋮
shuffle LIST
Returns the elements of LIST in a random order
@cards = shuffle 0..51 # 0..51 in a random order
⋮
这将是我的建议。
答案 2 :(得分:8)
我实际上有点惊讶你提议的洗牌有效。在Perl sort
函数的实现中,它尝试根据比较函数的值以升序获取数组的元素。问题是,你的比较函数没有返回一致的答案!有时它可能会说"foo" lt "bar"
,而有时可能会说"bar" lt "foo"
。这可能会使排序算法混淆到永不终止的程度,或者以致命错误或其他灾难性故障结束。
答案 3 :(得分:4)
sort
上的perl文档说明了这个
比较功能需要表现。如果它返回不一致的结果(有时说$ x [1]小于$ x [2]并且有时说相反,例如)结果没有明确定义。
这样做是个糟糕的主意。
ETA:我刚刚做了一个基准测试。在100000元素阵列上,使用FY-shuffle的速度也快10倍以上。
答案 4 :(得分:4)
首先,你知道无论你使用比较器,sort()都不可能比O(n log n)更快。因此,即使它执行的洗牌是公平的,其性能也会更差。
洗牌公平也是如此?对于一些(易于分析)排序算法来说,这显然是不公平的。考虑一个简单的冒泡排序 - 为了使一个元素从一端移动到另一端,比较函数必须评估n个连续调用的正数 - 在n个事件中应该是1的概率为1 ^ 2 ^ n。为了快速排序,很难分析,最终可能会变得公平。但是,如果它是正确的,那就做正确的方式。
答案 5 :(得分:3)
这只是直觉,但我认为使用这样的排序会产生一个集合,其顺序在某种程度上取决于原始集合的顺序。真正随机排序的结果不应完全取决于原始集合的顺序。我无法解释为什么/如何,也许其他人可以(或表明它实际上是随机的)?
至于它的效率如何,我不确定,但它可能不会比使用sort
的任何其他类型效率低很多,因为AFAIK rand()
相对便宜。不过我可能错了。
答案 6 :(得分:3)
有一个更好的Fisher-Yates shuffle函数,它不使用perlfaq4: How do I shuffle an array randomly?中内置的sort
。
答案 7 :(得分:0)
@shuffled = map {
$_->[1]
} sort {
$a->[0] <=> $b->[0]
} map {
[ rand(), $_ ]
} @array;