给定a的列表,请从列表中选择2个元素,将它们组合并添加到新列表中。重复直到新列表与输入列表大小相同

时间:2019-07-02 14:27:02

标签: haskell

我正在继续学习一些Haskell,目前我想实现一个非常简单且人为设计的遗传算法(我已经用其他几种语言完成了这件事)。尽管在Haskell,我现在正在努力解决一些最基本的问题,因为我不能停止必要的思考。

因此,作为遗传算法的一部分,您有一个选择和交叉阶段,在该阶段中,从当前种群中选择2个成员,将它们交叉以为新种群创建一个成员。重复此过程,直到新种群的大小等于输入种群的大小。

在Kotlin,C#,Java等中,解决方案将/可能看起来像这样:

val newPopulation = emptyList()
while (newPopulation.size < inputPopulation.size) {
    val mum = selectFrom(inputPopulation)
    val dad = selectFrom(inputPopulation)
    val child = mum.crossover(dad)
    newPopulation.add(child)
}
return newPopulation

但是,在Haskell中,您需要更少的命令性思考,而需要更多的映射声明。这就是我开始奋斗的地方。例如,没有“简单”的方法来声明新列表然后执行一会儿(newList.size

4 个答案:

答案 0 :(得分:2)

这是一个zip操作。通过从输入中每次采样n次来创建两个列表。将它们与df <- data.frame(values = sample (c(1:20), size = 1000, replace = T)) histogram <- df %>% ggplot(aes(x = values)) + geom_histogram(aes(y=..density..), alpha = 1,color = "dark grey", fill = "dark grey", position = "identity", binwidth = 1, size = 0) + theme_minimal() ggsave(histogram, file = "histogram.pdf") 组合。

输入采样是另一个挑战,说实话,要复杂得多,尤其是在考虑性能时。但是您还没有提供足够的细节来提出一种好的方法。

答案 1 :(得分:1)

首先,您可以可以在Haskell中强制执行此操作,这简直太可怕了(就像其他语言一样)。看起来像

import Data.IORef
import Control.Monad.Loops (whileM)

  do
    newPopulation <- newIORef []
    whileM_ (((<length inputPopulation).length<$>readIORef newPopulation) $ do
      mum <- selectFrom inputPopulation
      dad <- selectFrom inputPopulation
      let child = crossover mum dad
      modifyIORef (child:) newPopulation
    readIORef newPopulation

这里很尴尬:

  • while循环太笼统了。您已经预先知道它将进行多少次迭代,因此无需一遍又一遍地查询newPopulation长度作为停止条件!
  • 在每次迭代中,您只向结果中添加一个元素。好吧,当然有专用的控制结构,不需要手动进行危险的可变更新。

所以

import Control.Monad (replicateM)

  do
    replicateM (length inputPopulation) $ do
       mum <- selectFrom inputPopulation
       dad <- selectFrom inputPopulation
       let child = crossover mum dad
       return child

或更短

    replicateM (length inputPopulation) $ crossover
       <$> selectFrom inputPopulation<*>selectFrom inputPopulation

或者,您可以创建两个妈妈和爸爸的列表,然后将它们压缩为逐点交叉:

  do
    let n = length inputPopulation
    [mums,dads] <- replicateM 2 . replicateM n $ selectFrom inputPopulation
    return $ zipWith crossover mums dads

答案 2 :(得分:1)

如果要为每个现有生物构建一个新生物,则应遍历现有生物。因此,通过此样板,可以将您在问题中口头表达的内容进行存根:

import Control.Applicative
import Control.Monad.Random
import Data.Traversable

data Organism

crossover :: Organism -> Organism -> Organism
crossover = undefined

MonadRandom package提供了Control.Monad.Random模块。然后,您可以编写此代码,它可以很好地编译并具有正确的语义:

breed :: MonadRandom m => [Organism] -> m [Organism]
breed old = for old $ \_ -> liftA2 crossover (uniform old) (uniform old)

简洁明了,我认为它还是很可读的!

答案 3 :(得分:0)

这里您的要求草率地实现了。我没有访问System.Random模块的权限,因此省略了选择随机妈妈/爸爸的部分,但建议将其作为未经测试的代码。作为crossover,我正在使用串联。请注意,由于按索引访问链接列表时,由于性能问题,建议不要在列表中使用!!。如果需要,请尝试其他数据结构

import Data.Monoid ((<>))

{- you should use System.Random module in order to get random indices. 
The function should look similar to this but I couldn't test it:

getRandomIndex :: RandomGen g => g -> [a] -> [Int]
getRandomIndex g l = take hi $ randomRs (0, hi) g
  where hi = length l

In the main function use getStdGen to get a g to feed getRandomIndex 
-}

dads = ["dad0","dad1", "dad2", "dad3", "dad4", "dad5"]
mums = ["mum0","mum1", "mum2", "mum3", "mum4", "mum5"]

dadIndex :: [Int]
dadIndex = [1,4,2,3,2,0] -- let's imagine that this is the result of getRandomIndex g dads 
mumIndex :: [Int]
mumIndex = [0,2,2,3,5,2] -- let's imagine that this is the result of getRandomIndex g mums 

-- crossover is simply string concatenation
crossover :: String -> String -> String
crossover a b = a <> b

newPopulation :: [String]
newPopulation = zipWith crossover randomMums randomDads
  where randomMums = map (mums !!) mumIndex --
        randomDads = map (dads !!) dadIndex

main = print newPopulation