View difference between Paste ID: gdRKAQxt and
SHOW:
|
|
- or go back to the newest paste.
1 | - | |
1 | + | module Main (main) where |
2 | import Data.Char | |
3 | import Data.List | |
4 | import System.Random | |
5 | import Control.Monad | |
6 | import Control.Monad.State | |
7 | import Data.Function (on) | |
8 | targetStr = "Methinks it is like a weasel" | |
9 | targetLen = 28 | |
10 | populationSize = 500 | |
11 | replaceProbability = 1 / fromIntegral targetLen | |
12 | ||
13 | getRandom func | |
14 | = do (val, gen') <- liftM func get | |
15 | put gen' | |
16 | return val | |
17 | ||
18 | strDiff :: String -> Int | |
19 | strDiff = sum . zipWith (\ a b -> if a == b then 0 else 1) targetStr | |
20 | ||
21 | rndString :: State StdGen String | |
22 | rndString = replicateM targetLen rndChar | |
23 | ||
24 | rndChar :: State StdGen Char | |
25 | rndChar = getRandom (randomR (' ', '~')) | |
26 | ||
27 | rndMutateChar :: Char -> State StdGen Char | |
28 | rndMutateChar input | |
29 | = do roll <- getRandom (randomR (0, 1)) | |
30 | if (roll :: Double) <= replaceProbability then rndChar else | |
31 | return input | |
32 | ||
33 | rndMutateStr :: String -> State StdGen String | |
34 | rndMutateStr = mapM rndMutateChar | |
35 | ||
36 | crossoverStrings :: String -> String -> State StdGen String | |
37 | crossoverStrings str1 str2 | |
38 | = mapM | |
39 | (\ x -> | |
40 | do b <- getRandom random | |
41 | return (if b then fst x else snd x)) | |
42 | (zip str1 str2) | |
43 | ||
44 | uniqueRandoms :: Int -> State StdGen [Int] | |
45 | uniqueRandoms = go [] | |
46 | where go acc targetLen | |
47 | | length acc == targetLen = return acc | |
48 | | otherwise = | |
49 | do randVal <- getRandom (randomR (0, populationSize - 1)) | |
50 | go (if randVal `notElem` acc then randVal : acc else acc) targetLen | |
51 | ||
52 | choose a b = fst (if on (<=) snd a b then a else b) | |
53 | chooseR a b = fst (if on (>) (snd . snd) a b then a else b) | |
54 | ||
55 | withoutCrossover ::[(String, Int)] -> Int -> State StdGen (String, Int) | |
56 | withoutCrossover population count | |
57 | = do [i1, i2] <- liftM (map (population !!)) (uniqueRandoms 2) | |
58 | childStr <- rndMutateStr (choose i1 i2) | |
59 | let childDiff = strDiff childStr | |
60 | if childDiff == 0 then return (childStr, count) else | |
61 | do [r1, r2] <- liftM (map (ap (,) (population !!))) (uniqueRandoms 2) | |
62 | let (start, _ : end) = splitAt (chooseR r1 r2) population | |
63 | let newPopulation = start ++ ((childStr, childDiff) : end) | |
64 | withoutCrossover newPopulation (count + 1) | |
65 | ||
66 | withCrossover ::[(String, Int)] -> Int -> State StdGen (String, Int) | |
67 | withCrossover population count | |
68 | = do [i1, i2, i3, i4] <- liftM (map (population !!)) (uniqueRandoms 4) | |
69 | let parentA = choose i1 i2 | |
70 | let parentB = choose i3 i4 | |
71 | crossoverStr <- crossoverStrings parentA parentB | |
72 | childStr <- rndMutateStr crossoverStr | |
73 | let childDiff = strDiff childStr | |
74 | if childDiff == 0 then return (childStr, count + 1) else | |
75 | do [r1, r2] <- liftM (map (ap (,) (population !!))) (uniqueRandoms 2) | |
76 | let (start, _ : end) = splitAt (chooseR r1 r2) population | |
77 | let newPopulation = start ++ ((childStr, childDiff) : end) | |
78 | withCrossover newPopulation (count + 1) | |
79 | ||
80 | main | |
81 | = do gen <- newStdGen | |
82 | let individuals = replicate populationSize (fst (runState rndString gen)) | |
83 | let individualFitnesses = map (ap (,) strDiff) individuals | |
84 | newStdGen >>= print . fst . | |
85 | runState (withoutCrossover individualFitnesses populationSize) | |
86 | newStdGen >>= print . fst . | |
87 | runState (withCrossover individualFitnesses populationSize) |