Elm - 没有重复的随机数列表

Elm - list of random numbers without duplicates

我正在用 elm 制作游戏,并尝试将 N 个邪恶的机器人随机放置在具有 ROWS x COLS 单元格的网格上。

我想要的是一个 List (Int, Int) 对,指定放置 N 个机器人的位置。

我可以制作一个坐标对列表

makeGrid : Seed -> List (Int, Int)
makeGrid seed = 
  let gen = list n <| pair (int 0 rows) (int 0 cols)
in 
  fst (generate gen seed)

没关系。但是如果我想生成一个 unique 对的列表?

我是否应该执行命令式解决方案,让我保留一套我的东西并且 循环添加直到我有足够的?

可能是这样的(可能是错误的,没有在 REPL 中检查):

makeN : Int -> Seed -> (List (Int, Int), Seed)
makeN n seed = 
  let gen = list n <| pair (int 0 rows) (int 0 cols)
in 
  generate gen seed

makeGrid : List(Int, Int) -> Seed -> Int -> List (Int, Int)
makeGrid partial seed n =
  case of List.length partial 
    n -> partial
    current -> 
      let (new_elems, new_seed) = makeN (n - current) seed
      makeGrid Set.toList (Set.fromList <| append partial new_elems) new_seed n

这感觉不对。我想到了 3 个备选方案:

  1. 让我的网格成为 ROWS * COLS 坐标对的列表,类型为 List (Int, Int),然后将其打乱,并取列表中的前 N ​​对来放置我的机器人。这看起来非常简洁干净,但是 inefficient/bad 如果我需要的唯一点的数量比我的网格小得多,并且如果我的网格很大(因为 Fisher-Yates 是 O(n log(n)) 我认为).

  2. 使用this package之类的东西从我的网格中采样而无需替换,但我需要将我的网格更改为数组并且看起来它做了很多拆分和拼接数组操作,看起来很昂贵。

  3. 使用 JS FFI 在 4 行 JS 循环中实现。

None 这些解决方案感觉不错,我是否遗漏了什么? 我可能只是将游戏机制更改为每个单元格都有一个机器人在其上的概率 P,这样它更容易实现。

我打开了an issue on elm-random-extra, and mgold帮我开发了一个和题中类似的功能,然后添加到elm-random-extra 2.1.1中。非常感谢 mgold!

函数在 elm-random-extra 的 Random.Set 中是 set,类型为:

set : Int -> Generator comparable -> Generator (Set comparable)

你传给它一个n,一个generator,它return是一个generatorn你原来的[=15]的东西=].例如:

$ fst <| generate (set 5 <| int 0 1000) seed
Set.fromList [286,398,618,961,1000] : Set.Set Int

或者回答我原来的问题,在(例如)100x100 网格上的独特对:

$ fst <| generate (set 10 <| pair (int 0 100) (int 0 100)) seed
Set.fromList [(2,54),(4,55),(25,50),(35,32),(46,9),(55,9),(62,22),(65,77),(88,74),(95,31)]
: Set.Set ( Int, Int)

有一个警告:该函数不知道给定的生成器有多少个唯一元素,因此您有可能要求它提供 100 个唯一数字并给它一个骰子 (int 1 6 ) 生成器,它在尝试永远获取第 7 个唯一编号时卡住,可能导致堆栈溢出。

有两种选择:堆栈溢出时崩溃,或 return 错误数据,在多次罢工后提前停止。我们选择了第二个。如果它连续 10 次都找不到唯一的数字,它只会 return 目前为止找到的数字。我觉得这更符合elm的"No runtime errors"哲学