【发布时间】:2011-08-22 19:53:53
【问题描述】:
我正在编写一个遗传算法来生成字符串“helloworld”。但是当 n 大于等于 10,000 时,evolve 函数会产生堆栈溢出。
module Genetics where
import Data.List (sortBy)
import Random (randomRIO)
import Control.Monad (foldM)
class Gene g where
-- How ideal is the gene from 0.0 to 1.0?
fitness :: g -> Float
-- How does a gene mutate?
mutate :: g -> IO g
-- How many species will be explored?
species :: [g] -> Int
orderFitness :: (Gene g) => [g] -> [g]
orderFitness = reverse . sortBy (\a b -> compare (fitness a) (fitness b))
compete :: (Gene g) => [g] -> IO [g]
compete pool = do
let s = species pool
variants <- (mapM (mapM mutate) . map (replicate s)) pool
let pool' = (map head . map orderFitness) variants
return pool'
evolve :: (Gene g) => Int -> [g] -> IO [g]
evolve 0 pool = return pool
evolve n pool = do
pool' <- compete pool
evolve (n - 1) pool'
使用species pool = 8,8 个基因库复制到 8 个组。每组变异,每组中最适者被选为进一步进化(回到8个基因)。
【问题讨论】:
-
假设您使用
ghc -O2作为编译器,您的第一个版本在evolve函数中没有任何堆栈溢出。既然看不到compete的实现,只能这么说。 -
上面提供的 GitHub 链接 (github.com/mcandre/genetics) 指定了
compete以及一个确实使用ghc -O2的 Makefile。我认为第一个示例中的<-足以防止堆栈溢出。我不确定问题出在哪里。 -
>>= 应该等于 do 表示法,没有区别。
-
正如 dons 所指出的,问题完全出在竞争函数上,毫无疑问,该函数会在彼此之上运行多次,从而构建一个具有一大串 thunk 的数据结构。我确信 github 链接具有必要的数据,但对于那些希望帮助您在此处发布一个完整的工作示例来展示您的问题的人会更有帮助——最好是尽可能减少。与此同时,快速+肮脏的解决方案是在每次递归调用时 rnf 或以其他方式强制您的池。
-
嗯,他们为什么首先在这里使用 IO monad?看起来丑陋且不习惯。他们可能应该改用随机单子,或者直接向变异函数提供随机数。
标签: memory haskell random stack-overflow genetic-algorithm