【问题标题】:How to shuffle list in O(n) in OCaml?如何在 OCaml 中的 O(n) 中洗牌?
【发布时间】:2013-02-12 06:47:47
【问题描述】:

在 O(n) 中洗牌 array 并不难,通过就地交换,

如何在 OCaml 中为 list 使用 O(n)?


要求:

  1. 无数组或就地使用

  2. 将此视为面试问题

【问题讨论】:

  • 廉价技巧:将列表复制到数组、随机播放、复制回......应该是 O(n)。
  • @phimuemue 不,当然没有数组或位置,将其视为面试问题
  • 是否有要求,例如列表的所有可能排列必须具有相同的可能性?
  • 如果没有突变,我认为没有比O(n log n) 做得更好的了。
  • @phimuemue 当然,这意味着“纯”随机洗牌

标签: functional-programming ocaml


【解决方案1】:

列表是不可变的,使用不可变数据通常需要付出 log n 的代价。如果您愿意支付此费用,则有一个明显的 n log n 方法:用随机值标记每个列表元素,根据随机值排序,删除随机值。这就是我在生产代码中打乱列表的方式。

这是我销售的 iOS 应用程序的随机播放代码:

let shuffle d =
    let nd = List.map (fun c -> (Random.bits (), c)) d in
    let sond = List.sort compare nd in
    List.map snd sond

【讨论】:

  • 是的,我也这么认为。有趣的是,当我要编写快速排序时,这个问题突然出现在我面前。对于quicksort,我先将列表洗牌,然后进行分区等。在这种情况下,如何对代码中的随机值进行排序?
  • 请注意,如果您使用排序进行洗牌,并非所有排列的可能性都相同。看到这个:okmij.org/ftp/Haskell/perfect-shuffle.txt
  • 标签相等时似乎会出现偏差。对于 63 位标签,这是一个很小的偏差。 (我从 Oleg 那里读到了这条消息。他很棒。)
  • @sepp2k 我认为消息所讨论的 attach_value-then-sort 算法的缺点很容易是 fixed bywhile sorting the attached values, if two values are equal, then generate another 0|1 to decide who comes in front,即我们不想要稳定的排序。我对么?无论如何,如果使用稳定排序,该消息是正确的
  • 是的,我总是忘记 List.map、List.filter、List.fold 的东西。必须多练习。
【解决方案2】:

你可以模仿纸牌的洗牌。

一副纸牌的洗牌意味着:

  • 将甲板分成两部分
  • 将两部分交错

其实做反向排列更容易:

  • 有两个辅助列表 A 和 B,遍历原始列表 L 并将每个元素随机(概率为 1/2)推到 A 或 B 前面。
  • L := List.rev A @ List.rev B(这可以使用自定义 List.rev 进行尾递归)。
  • 重复 k 次。

根据“Persi Diaconis,2002 年从浅滩洗牌分析中获得的数学发展”,选择 k = 3/2 log_2(n) + c。事实上,均匀性和结果之间的总变化距离呈指数级快速下降到 0:每次增加 c 时,它大约减半。你可以选择 c=10。

空间 O(1)(如果你破坏 L),时间 O(n log n)。但是对随机生成器有 O(n log n) 次调用,而 Jeffrey Scofield 的解决方案只需要 O(n) 个随机位,但需要 Θ(n) 个空间。

【讨论】:

  • List.rev_append 是尾递归的
猜你喜欢
  • 2013-09-02
  • 2022-06-28
  • 1970-01-01
  • 2021-04-09
  • 1970-01-01
  • 1970-01-01
  • 2012-08-23
  • 2012-05-09
  • 1970-01-01
相关资源
最近更新 更多