3

我正在尝试打乱列表的元素:

(* Returns a list with the same elements as the original but in randomized order *)
let shuffle items = 
    items
    |> List.map (fun x -> (x, System.Random().Next()))
    |> List.sortBy snd
    |> List.map fst

但是,这总是items以相同的顺序返回,因为:

> List.map (fun x -> x, System.Random().Next()) [1; 2; 3];;
val it : (int * int) list = [(1, 728974863); (2, 728974863); (3, 728974863)]

> List.map (fun x -> x, System.Random().Next()) [1; 2; 3];;
val it : (int * int) list =
  [(1, 1768690982); (2, 1768690982); (3, 1768690982)]

> List.map (fun x -> x, System.Random().Next()) [1; 2; 3];;
val it : (int * int) list = [(1, 262031538); (2, 262031538); (3, 262031538)]

为什么System.Random().Next()每次调用总是返回相同的值?是因为连续的电话在时间上太接近了吗?还是我以其他方式滥用 API?

(注意:这个答案对我来说很好,但我很好奇为什么会出现这种行为。)

4

3 回答 3

9

最好通过System.Random() 的默认构造函数的手册来解释;

默认种子值来自系统时钟并且具有有限的分辨率。因此,通过调用默认构造函数连续创建的不同 Random 对象将具有相同的默认种子值,因此将产生相同的随机数集。

于 2012-08-15T18:46:53.673 回答
4

要记住的一件事是,您不是从随机数生成器生成数字序列,而是创建一个随机数生成器序列并生成每个随机数的第一个随机数。

请记住,它System.Random().Next()是 的简写(new System.Random()).Next(),因此您在每次迭代时使用 Random 的默认构造函数创建一个新的 System.Random 对象。如其他答案中所述,默认构造函数使用当前时间的粗略值作为 RNG 的初始种子,因此当快速连续调用时,基本上每次都会重新创建相同的 RNG(这将在其上生成相同的数字第一次也是唯一一次调用)。

解决方案是只创建一个 System.Random 对象并重用它:

> let rng = new System.Random() in List.map (fun x -> x, rng.Next()) [1; 2; 3];;
val it : (int * int) list = [(1, 483259737); (2, 719806274); (3, 1951956175)]
于 2012-08-15T21:15:34.663 回答
3

它之所以这样工作,是因为您每次都使用相同的种子值。这就是为什么连续调用Random.

于 2012-08-15T18:46:04.993 回答