
Erlend Hamberg
g <- getSplit let genome' = evalRand (mutate genome) g
Handling the random generator explicitly like this feels a bit ugly. Or is this okay?
I was working on a very similar problem and I used 'split' of RandomGen all the way down. It behaved well as far as I can tell, and if I recall correctly, 'split' was faster than 'next' for StdGen. So, I'd also appreciate any comment on the elegance issue myself. :-) Though, I didn't quite get why you are explicitly calling 'evalRand' instead of utilizing MonadRandom. Here's an example:
makePopulation :: (RandomGen g) => [(Genome, Fitness)] -> Rand g [Genome] makePopulation chms = do g <- getSplit return $ makePopulation' chms g [] where size = length chms makePopulation' c g' a = if length a == size -- if we have ‘size’ members, we are done then a -- if not, find two new parents and append a child else makePopulation' c g' newPop where parent1 = evalRand (rouletteSelect chms) g' parent2 = evalRand (rouletteSelect chms) g' c1 = evalRand (reproduce (parent1, parent2) mutationRate) g' newPop = c1:a
Here, you're generating both parents using the same random generator, hence, parents are always identical. Here's a variation that works within MonadRandom:
makePopulation :: (RandomGen g) => [(Genome, Fitness)] -> Rand g [Genome] makePopulation chms = makePopulation' [] where size = length chms makePopulation' a = if length a == size -- if we have ‘size’ members, we are done then return a -- if not, find two new parents and append a child else do parent1 <- rouletteSelect chms parent2 <- rouletteSelect chms c1 <- reproduce (parent1, parent2) mutationRate let newPop = c1:a makePopulation' newPop
I didn't check for bugs in the algorithm but now both parents and the offspring are generated independently, and resulting fitness values won't be identical. I think the idea is that you should be able to reduce the number of 'evalRand's in your module to one. -- Gökhan San