Can this random number generator logic be simplified? - f#

I am learning F# and would like to know if the following logic to generate random numbers is acceptable.
Could this be written in a more maintainable fashion?
Is this code safe?
let hashset = System.Collections.Generic.HashSet<int>()
let mutable continueLooping = true
while (continueLooping) do
let value = System.Random().Next(0, 12)
let success = hashset.Add(value)
continueLooping <- hashset.Count <> 12
UPDATE
let hashset = System.Collections.Generic.HashSet<int>()
let randomGenerator = System.Random()
let mutable continueLooping = true
let expectedLength = 12
while (continueLooping) do
let value = randomGenerator.Next(0, expectedLength)
let success = hashset.Add(value)
continueLooping <- hashset.Count <> expectedLength

You could define a helper function to perform a Fisher-Yates shuffle. This shuffle function is pretty generally useful since it will work on any seq<'a> so you have plenty of opportunities to reuse it.
// shuffle a sequence into random order
let shuffle xs =
// swap two elements in the supplied array
let swap i j (array : _[]) =
let tmp = array.[i]
array.[i] <- array.[j]
array.[j] <- tmp
let rnd = System.Random()
let xArray = Seq.toArray xs
let n = Array.length xArray
for i in [0..(n-2)] do
let j = rnd.Next(i, n-1)
swap i j xArray
xArray |> Seq.ofArray
Then just apply it to a list or something using
let shuffledList = [0..11] |> shuffle |> Seq.toList

Related

Why does this change the order of evaluation in F#

I am working through the Project Euler puzzles (https://projecteuler.net/) using F#. It turns out that a lot of the puzzles require the generation of prime numbers, which is an expensive computation.
To make my life easier, I want to create a module that will generate prime numbers, but also write them to a text file so I just look them up the next time I need them.
Here is my prime generator module:
module Utilities
open System
open System.IO
open System.Reflection
module Primes =
//This is where the cache file is
let private cachePath =
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
+ "\PrimeCache.txt"
//Parses the file to an int list
let private readCache : int list =
let text = File.ReadAllText(cachePath).Trim()
text.Split([|','|])
|> Seq.filter(fun w -> w |> String.length > 0)
|> Seq.map(fun w -> Int32.Parse(w))
|> Seq.toList
//Writes an int list to the file
let writeCache cache =
let text = String.Join(",", cache |> Seq.map(fun n -> n.ToString()))
File.WriteAllText(cachePath, text)
//In-memory cache
let mutable cache = []
cache <- readCache
let private appendElement element list =
element::(list |> List.rev) |> List.rev
let private isPrimeInner (n : int) =
if n < 2 then false
else
let len = cache.Length
//Factor can't be greater than the square root
let factorLimit = Convert.ToInt32(Math.Ceiling(Math.Sqrt(Convert.ToDouble(n))))
let mutable i = 0
let mutable stop = false
let mutable isPrime = true
while stop = false do
//Get the next element from cache
let p = cache.[i]
i <- i+1
//Don't go past last index of cache
if i >= len
then stop <- true
else ()
//Don't check primes > sqrt(n)
if p > factorLimit
then stop <- true
else ()
//If its divisible by any prime, its not a prime
if n % p = 0
then
stop <- true
isPrime <- false
else ()
//Add primes to cache
if isPrime
then cache <- appendElement n cache
else ()
isPrime
let isPrime (n : int) : bool =
let result = isPrimeInner n
result
let getPrimes : int seq =
seq {
for p in cache do
yield p
let next = if cache.Length > 0
then cache.[cache.Length-1]+1
else 2
for n = next to Int32.MaxValue do
if isPrimeInner n
then yield n
else ()
}
let saveCache : unit =
writeCache cache
Here is the code for the problem I am currently working on that consumes this. (The actual problem is to sum all primes under 2,000,000, but I am testing on 1000 for now):
module Problem10
let getAnswer =
let limit = 1000
let primes = Utilities.Primes.getPrimes
|> Seq.takeWhile(fun p -> p < limit)
let result = primes |> Seq.sum
Utilities.Primes.saveCache
result
This will give the right answer, but will save an empty cache file back to the disk. If I step through it in the debugger, the call to saveCache is one of the first things that is hit, before any breakpoints I set inside isPrimeInner. If I switch the calling code to this:
module Problem10
let getAnswer =
let limit = 1000
let primes = Utilities.Primes.getPrimes
|> Seq.takeWhile(fun p -> p < limit)
let result = primes |> Seq.sum
let x = Utilities.Primes.cache
Utilities.Primes.writeCache x
result
the cache will be properly saved with new primes.
I've seen this kind of behavior before in C# when misusing iterator blocks. I don't understand how these two versions of the calling code are any different at runtime. What am I doing wrong here?

Second Taxicab Number Generator

I am attempting to generate a series of guesses for the second Taxicab number. What I want to do is is call the Attempt function on a series of integers in a finite sequence. I have my two questions about implementation in the comments.
A taxi cab number, in case your wondering, is the least number that satisfied the sum of 2 unique cubes in for n unique sets of 2 unique cubes. Ta(2) is 1729.
[<EntryPoint>]
let main argv =
let Attempt (start : int) =
let stop = start+20
let integerList = [start..stop]
let list = List.init 3 (fun x -> integerList.[x])
//Is there a simple way to make initialize the list with random indices of integerList?
let Cube x = x*x*x
let newlist = list |> List.map (fun x -> Cube x)
let partitionList (x : List<int>) (y : int) = List.sum [x.[y];x.[y+1]]
let intLIST = [0..2]
let partitionList' = [for i in intLIST do yield partitionList newlist i]
let x = Set.ofList partitionList'
let y = Set.ofList partitionList'
//I was going to try to use some kind of equality operator to determine whether the two sets were equal, which could tell me whether we had actually found a Taxicab number by the weakened definition.
System.Console.Write(list)
System.Console.Write(newlist)
let rnd = System.Random()
//My primary question is how can I convert a random to an integer to use in start for the function Attempt?
System.Console.ReadKey() |> ignore
printfn("%A") argv
0
Dirty way to initialize list with random indexes of another list:
let randomIndexes count myList =
let rand = System.Random()
seq {
for n = 1 to count do
yield rand.Next(List.length myList) }
|> Seq.distinct
//|> Seq.sort // if you need them sorted
|> List.ofSeq
let result = randomIndexes 5 [3;2;4;5]
printfn "%A" result

How to make an array shuffle function yield different results on subsequent calls [duplicate]

This question already has answers here:
F# getting a list of random numbers
(6 answers)
Closed 8 years ago.
I have the following function which works how I want for a single call:
let shuffle (arr : 'a array) =
let array = Array.copy arr
let rng = new Random()
let n = array.Length
for x in 1..n do
let i = n-x
let j = rng.Next(i+1)
let tmp = array.[i]
array.[i] <- array.[j]
array.[j] <- tmp
array
However, for multiple calls as in the following (the x isn't used for anything), it yields the same shuffle for each call. How do I make it yield a different shuffle each time?
[for x in 1..3 do yield shuffle [|1;2;3|]]
>
val it : int [] list = [[|1; 3; 2|]; [|1; 3; 2|]; [|1; 3; 2|]]
You want to move the random outside the function like so:
let rng = new Random()
let shuffle (arr : 'a array) =
let array = Array.copy arr
let n = array.Length
for x in 1..n do
let i = n-x
let j = rng.Next(i+1)
let tmp = array.[i]
array.[i] <- array.[j]
array.[j] <- tmp
array
The reason is that the RNG is seeded by the time by default, which in a tight loop doesn't change enough. Moving the rng outside the function means that it persists across calls.

Sampling in F# : is Set adequate?

I have an array of items, from which I'd like to sample.
I was under the impression that a Set would the a good structure to sample from, in a fold where I'd give back the original or a modified set with the retrieved element missing depending if I want replacement of not.
However, there seems to no method to retrieve an element directly from a Set.
Is there something I am missing ? or should I use Set of indices, along with a surrogate function that starts at some random position < Set.count and goes up until it finds a member ?
That is, something along this line
module Seq =
let modulo (n:int) start =
let rec next i = seq { yield (i + 1)%n ; yield! next (i+1)}
next start
module Array =
let Sample (withReplacement:bool) seed (entries:'T array) =
let prng, indexes = new Random(seed), Set(Seq.init (entries |> Array.length) id)
Seq.unfold (fun set -> let N = set |> Set.count
let next = Seq.modulo N (prng.Next(N)) |> Seq.truncate N |> Seq.tryFind(fun i -> set |> Set.exists ((=) i))
if next.IsSome then
Some(entries.[next.Value], if withReplacement then set else Set.remove next.Value set)
else
None)
Edit : Tracking positively what I gave, instead of tracking what I still can give would make it simpler and more efficient.
For sampling without replacement, you could just permute the source seq and take however many elements you want to sample
let sampleWithoutReplacement n s =
let a = Array.ofSeq s
seq { for i = a.Length downto 1 do
let j = rnd.Next i
yield a.[j]
a.[j] <- a.[i - 1] }
|> Seq.take n
To sample with replacement, just pick a random element n times from the source seq
let sampleWithReplacement n s =
let a = Array.ofSeq s
Seq.init n (fun _ -> a.[rnd.Next(a.Length)])
These may not be the most efficient methods with huge data sets however
Continuing our comments...if you want to randomly sample a sequence without slurping the entire thing into memory you could generate a set of random indices the size of your desired sample (not too different from what you already have):
let rand count max =
System.Random()
|> Seq.unfold (fun r -> Some(r.Next(max), r))
|> Seq.distinct
|> Seq.take count
|> set
let takeSample sampleSize inputSize input =
let indices = rand sampleSize inputSize
input
|> Seq.mapi (fun idx x ->
if Set.contains idx indices then Some x else None)
|> Seq.choose id
let inputSize = 100000
let input = Seq.init inputSize id
let sample = takeSample 50 inputSize input
printfn "%A" (Seq.toList sample)

F# solution for Store Credit

I want to solve this excercise: http://code.google.com/codejam/contest/351101/dashboard#s=p0 using F#.
I am new to functional programming and F# but I like the concept and the language a lot. And I love the codejam excercise too it looks so easy but real life. Could somebody point me out a solution?
At the moment I have written this code which is just plain imperative and looks ugly from the functional perspective:
(*
C - Credit
L - Items
I - List of Integer, wher P is single integer
How does the data look like inside file
N
[...
* Money
* Items in store
...]
*)
let lines = System.IO.File.ReadAllLines("../../../../data/A-small-practice.in")
let CBounds c = c >= 5 && c <= 1000
let PBounds p = p >= 1 && p <= 1000
let entries = int(lines.[0]) - 1
let mutable index = 1 (* First index is how many entries*)
let mutable case = 1
for i = 0 to entries do
let index = (i*3) + 1
let C = int(lines.[index])
let L = int(lines.[index+1])
let I = lines.[index+2]
let items = I.Split([|' '|]) |> Array.map int
// C must be the sum of some items
// Ugly imperative way which contains duplicates
let mutable nIndex = 0
for n in items do
nIndex <- nIndex + 1
let mutable mIndex = nIndex
for m in items.[nIndex..] do
mIndex <- mIndex + 1
if n + m = C then do
printfn "Case #%A: %A %A" case nIndex mIndex
case <- case + 1
I would like to find out items which add up to C value but not in a usual imperative way - I want functional approach.
You don't specify how you would solve the problem, so it's hard to give advices.
Regarding reading inputs, you can express it as a series of transformation on Seq. High-order functions from Seq module are very handy:
let data =
"../../../../data/A-small-practice.in"
|> System.IO.File.ReadLines
|> Seq.skip 1
|> Seq.windowed 3
|> Seq.map (fun lines -> let C = int(lines.[0])
let L = int(lines.[1])
let items = lines.[2].Split([|' '|]) |> Array.map int
(C, L, items))
UPDATE:
For the rest of your example, you could use sequence expression. It is functional enough and easy to express nested computations:
let results =
seq {
for (C, _, items) in data do
for j in 1..items.Length-1 do
for i in 0..j-1 do
if items.[j] + items.[i] = C then yield (i, j)
}
Seq.iteri (fun case (i, j) -> printfn "Case #%A: %A %A" case i j) results

Resources