我正在编写一种遗传算法来生成字符串“ 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个基因)。

的GitHub

===============>>#1 票数:3

如果您对性能感兴趣,可以使用快速随机数生成器,例如:

其次,尽管看起来有些庞大的结构,但是compete看起来非常可疑,因为它完全是懒惰的。 尝试使用deepseq锤将其重写为更严格一点

import Control.DeepSeq    

compete :: (Gene g, NFData 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
    pool' `deepseq` return pool'

不过,这些东西都不需要放在IO中(单独的问题)。 Rand monad这样的东西可能更适合

===============>>#2 票数:2 已采纳

多亏了Don的deepseq建议,我得以将问题缩小到mapM mutate ,这导致了太多的问题。 新版本具有mutate' ,它使用seq防止变形。

module Genetics where

import Data.List (maximumBy)
import Random (randomRIO)

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 in each round?
    species :: [g] -> Int

best :: (Gene g) => [g] -> g
best = maximumBy (\a b -> compare (fitness a) (fitness b))

-- Prevents stack overflow
mutate' :: (Gene g) => g -> IO g
mutate' gene = do
    gene' <- mutate gene
    gene' `seq` return gene'

drift :: (Gene g) => [[g]] -> IO [[g]]
drift = mapM (mapM mutate')

compete :: (Gene g) => [g] -> IO [g]
compete pool = do
    let islands = map (replicate (species pool)) pool
    islands' <- drift islands
    let representatives = map best islands'
    return representatives

evolve :: (Gene g) => Int -> [g] -> IO [g]
evolve 0 pool = return pool
evolve n pool = compete pool >>= evolve (n - 1)

的GitHub

===============>>#3 票数:1

代替使用orderFitnesssortBy (map head . map orderFitness) ,您可以使用maximumBy和一个map 这样做并不会节省太多(因为您是从O(n log n)到O(n),并且可能会从消除双重映射中得到另一个因子2),但是至少在某种程度上更简单,更有效。 您还将摆脱对反向调用的要求。

我怀疑这是否可以解决deepseq问题,但是仍然应该是一个改进。

编辑:如果标准库和GHC是完美的,则head . sortBy head . sortBy将产生与maximumBymap head . map sortBy相同的代码map head . map sortBy 不幸的是, map head . map sortBy将产生相同的代码来map (head . sortBy) ,但实际上这两种情况都不是真的。 sortBy往往会做大量额外的内存分配,因为它是分而治之的算法。 组合地图有时是一种优化,但不应指望。

更重要的是,使用maximumBy更具声明性。 可以很容易地看到代码的功能以及所需的时间。 利用优化也应该更容易,因为我们知道目标是什么,而不仅仅是我们如何实现它。

  ask by mcandre translate from so

未解决问题?本站智能推荐:

1回复

MonadRandom:为什么会发生堆栈溢出?

这个问题肯定是针对stackoverflow.com的 这是样本 编译后返回 怎么会发生? MonadRandom是不是很懒?还是其他呢? 以及在这种情况下如何定义堆栈溢出的原因?
2回复

Haskell foldl和堆栈溢出?

我读了一篇帖子声称foldl可能很容易发生堆栈溢出。 发布示例代码是: 代码不会在我的机器中溢出。 但它可能因环境而异。 我增加了这样的数字: 它导致硬盘交换,所以我不得不停止评估。 示例代码并不重要。 堆栈溢出真的发生了吗? 或者只是一个过去的故事?
1回复

Haskell递归堆栈溢出

我对Haskell来说还很陌生,非常抱歉。 但是-如何摆脱无尽的递归而不会被溢出。 这是代码: 编辑 : 我相信代码是不言自明的,但是:如果x等于2,则除以2,否则3 * x +1。这是著名的Collat​​z问题的一部分。
2回复

Haskell - 递归堆栈溢出

我试图将所有n从1加到一个非常大的数字(现在是10 ** 9),但它会给出堆栈溢出。 另外我不认为在1处停止并且在不同行中执行总和n是最有效的方式,但下面的代码是我对Haskell的全部知识。 我真的不太了解函数式编程,我想尽可能多的解释。 (我也试过把$!strict放在最后一行,这在其
4回复

Haskell,memoization,堆栈溢出

我正在研究Project Euler的问题14( http://projecteuler.net/problem=14 )。 我正在尝试使用memoization,以便将给定数字的序列长度保存为部分结果。 我正在使用Data.MemoCombinators。 下面的程序产生堆栈溢出。
3回复

调试Haskell中的堆栈溢出

我是Haskell和函数式编程的新手,我有一个可以运行的程序,但几秒钟后溢出了堆栈。 我的问题是,我应该从这里做什么? 如何至少知道它发生的位置,打印纸叠或其他内容? 当使用:trace在ghci中运行时,该程序非常慢,因此不会发生堆栈溢出。 对于runhaskell也不会发生这种情
2回复

递归函数haskell上的堆栈溢出

这是Haskell中幂函数的一种实现。 但是我有堆栈溢出错误。 不知道如何解决
2回复

Haskell中是否发生堆栈溢出错误?

作为一种纯函数式编程语言,Haskell集中使用递归。 Haskell中是否出现堆栈溢出错误,就像在Java中一样? 为什么或者为什么不?
1回复

如何避免Haskell中的堆栈溢出?

Haskell不支持循环计算,而是提供使用递归算法。 但是这种方法导致堆栈增长,甚至堆栈溢出。 我认为应该有办法解决这个问题。 这是样本。 我想知道,每5秒可以调用多次getClockTime : 该程序耗时5秒,但最终我得到了: 堆栈空间溢出:当前大小为8388608
2回复

为什么这个Haskell代码会产生堆栈溢出?

我第一次和Haskell一起玩。 我写了这三行,期望得到编译器错误,但在ghci键入它会导致堆栈溢出。 谁能解释这种行为?