我正在继续学习一些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
答案 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