如何在OCaml中将O(n)中的列表混洗?

时间:2013-02-26 17:33:36

标签: functional-programming ocaml

在O(n)中使用array进行随机交换并不难,

如何在OCaml中为list执行此操作,使用O(n)?


要求:

  1. 没有数组或就地使用

  2. 将此视为面试问题

2 个答案:

答案 0 :(得分:10)

列表是不可变的,并且通常需要支付 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

答案 1 :(得分:0)

你可以模仿卡片的浅滩洗牌。

一副纸牌的轻微洗牌意味着:

  • 分两部分切割甲板
  • 交错两部分

反向排列实际上更容易:

  • 有两个辅助列表A和B,通过原始列表L,并在A或B前面随机推送每个元素(概率为1/2)。
  • 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)空间。