Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

random generation into fsharp list

Tags:

random

f#

I am trying to use F# to generate a list of random triples -> two random numbers and their sum:

let foo minNum maxNum = 
    let rnd = System.Random()
    let first = rnd.Next(minNum, maxNum)
    let second = rnd.Next(minNum, maxNum)
    let third = first + second
    first, second, third

which can be called like this and works well (always gives a new random number) (when using F# interactive)

foo 0 50

When trying to generate a list of random triples like this

List.init 100 (fun index -> foo 0 50)

I would like this to be a list of 100 randomized triples, but instead they all come out with the same value. I can see that the function doesn't depend on the index and therefore does not need to recalculate, but I am not sure how to work around it (I tried introducing the index as an unused dummy variable, also tried introducing the index as a random seed, but neither helped)

like image 278
Henrik K Avatar asked Aug 13 '11 15:08

Henrik K


3 Answers

You're assuming that the reason that your code does not work like you want it to is that the compiler erroneously applies some sort of common subexpression elimination, which causes foo 0 50 to be evaluated only once. This is not the case. The compiler knows very well that functions may be impure and won't perform optimizations that would break with impure code (unless the specific code that is being optimized can be proven to be pure). So you don't have to worry about making the compiler think that you're using the argument.

The problem isn't that foo 0 50 is called only once - it is indeed called 100 times like you want. The problem is that it returns the same value each of the 100 times it is called.

But why would it do that when it returns different values when you test it manually?

Because you create a new Random object every time you call foo. Since you don't provide a seed value to that random object, it will be seeded with the current system time. So if you call the function multiple times with some time passing in between, it will return different values each time. However if you call it 100 times in such a short amount of time that the system time does not change in between, you'll get back the same value 100 times.

So the solution to your problem is to reuse the same random object, instead of creating a new one each time foo is called.

like image 121
sepp2k Avatar answered Nov 15 '22 11:11

sepp2k


Try this instead. It will reuse the same random object without you having to keep one around in your main scope:

let foo  = 
    let rnd = System.Random()
    fun  minNum maxNum ->
        let first = rnd.Next(minNum, maxNum)
        let second = rnd.Next(minNum, maxNum)
        let third = first + second
        first, second, third

Also note that Next is not threadsafe so if you intend to use foo from several threads you need to add some locking to Next.

like image 42
alun Avatar answered Nov 15 '22 09:11

alun


To complement sepp2k answer with some code:

let rnd = System.Random()

let foo minNum maxNum = 
    let first = rnd.Next(minNum, maxNum)
    let second = rnd.Next(minNum, maxNum)
    let third = first + second
    first, second, third
like image 31
svick Avatar answered Nov 15 '22 11:11

svick