System.Random().Next() 返回相同的结果



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

(* 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?

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

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

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

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

请记住,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)]

它之所以如此工作,是因为您每次都使用相同的种子值。这就是为什么对同一Random实例进行连续调用很重要的原因。

最新更新