F#中的随机数组元素

时间:2011-01-09 19:17:55

标签: f#

我试图在F#上学习自己,我担心我不会理解我应该做的事情。

我正在尝试重新创建一本我喜欢的书的功能(来自Royal和Panarese的Creative Cursing)。

简而言之,您有两个单独的单词列表,可以从中选择两个随机单词,从而产生一个奇怪的短语。够简单吗?

这就是我所拥有的:


#light

open System
open System.IO

let getWordList file =
  File.ReadAllLines( file )

let getRandArrElement (arr : string[]) =
  let rnd = Random( 0 )
  arr |> Seq.nth (rnd.Next arr.Length)

let wordList1 = getWordList "words1.txt"
let wordList2 = getWordList "words2.txt"

let word1 = getRandArrElement wordList1
let word2 = getRandArrElement wordList2

printf "%s %s" word1 word2

也有效。除了它每次运行时返回相同的短语。

我感觉它正在做的是在编译时每次调用“getRandArrElement”计算一个随机值,然后使用该值作为THE值(我认为这很奇怪,但我知道什么?)。

我的逻辑有什么问题,我该如何解决?

2 个答案:

答案 0 :(得分:10)

你的问题在这里:

let getRandArrElement (arr : string[]) =
  let rnd = Random( 0 )
  arr |> Seq.nth (rnd.Next arr.Length

Random数字并非真正随机。他们获取种子值,计算0.01.0之间的随机数;新值用作下一个种子。换句话说,Random我确切地说是确定性的,所以反复播种相同的值会产生相同的输出序列。

由于你总是构造一个具有相同种子的新Random,你每次都会得到与输出相同的随机数。

我建议做一些改进:

  • 使用let rnd = Random()。默认构造函数使用系统时钟作为种子,因此您将获得不同的序列。 (它仍然可以获得相同的序列。系统时钟的分辨率大约为10毫秒,因此在该区间内构造两个Random将很有可能以相同的值播种。

    如果您使用let rnd = Random(0),即使rnd超出了您的职能范围,每次运行您的计划时,您都会按完全相同的顺序获得相同的句子。

  • 您可以将rnd的声明移到您的功能之外,这样您就不会一遍又一遍地构建它。作为替代方案,您可以写下:

    let getRandArrElement =
        let rnd = Random()
        fun (arr : string[]) -> ...
    

    当您打开模块时,F#会执行所有无参数值,因此会立即分配rnd,并为getRandArrElement分配值fun (arr : string[]) -> ...

  • 使用arr.[index]代替arr |> Seq.nth (rnd.Next arr.Length)。它不仅更简洁,而且还是O(1)。 Seq.nth将它视为一个序列,它一次遍历一个元素,直到它到达与给定索引匹配的元素,从而进行操作O(n)

最终结果应该是:

let getRandArrElement =
  let rnd = Random()
  fun (arr : string[]) -> arr.[rnd.Next(arr.Length)]

答案 1 :(得分:3)

每次使用具有相同种子的新Random,这是预期的行为 - 如果重复使用相同的种子,则生成相同的数字序列。我建议你将rnd的声明移出函数,这将解决你的问题:

let rnd = Random();

let getRandArrElement (arr : string[]) =
  arr |> Seq.nth (rnd.Next arr.Length)