为什么一个配合功能会更快地收敛到解决方案?

时间:2011-02-03 08:20:25

标签: c haskell genetic-algorithm

我曾经两次使用遗传算法,这是一个问候世界的教程 和一个tsp解算器。我试图复制haskell中的hello world教程, 并看看两者如何快速比较。 haskell实现是 相当慢,但让我感到烦恼的是C版汇聚了很多 更快(大约40代)没有任何突变。 haskell版本 AFAIK具有更好的配偶功能(倾向于更好的一面) 人口)并在大约60代收敛,但只有在有 涉及突变。如果没有突变,它很快会停在局部最大值。

haskell版本具有更好的配合功能,但需要突变 甚至收敛; C版本没有突变和更糟糕的配偶功能但是 收敛得更快。

randomSt :: (RandomGen g, Random a) => State g a
randomSt = state random
randomRSt :: (RandomGen g, Random a) => (a, a) -> State g a
randomRSt = state . randomR
wrandomRSt :: (RandomGen g) => Int -> State g Int
wrandomRSt n =
  let s = liftM2 (+) (randomRSt (0.0, 1.0)) (randomRSt (0.0, 1.0)) :: (RandomGen g) => State g Float
      n' = fromIntegral n
  in liftM (flip div 2 . floor . abs . subtract (n' / 2) . (n' *)) s

mateCopy :: (RandomGen g) => StringVector -> State g (StringVector)
mateCopy xs = V.replicateM population (step xs)
  where
    step :: RandomGen g => StringVector -> State g (Vector Char)
    step xs =
      let mom = liftM (xs !) (randomRSt (0,population `div` 2))
          dad = liftM (xs !) (randomRSt (0,population `div` 2))
          split = randomRSt (0, V.length target - 1)
      in do
        m <- mom
        d <- dad
        s <- split
        return (V.take s m V.++ V.drop s d)

mate :: (RandomGen g) => StringVector -> State g (StringVector)
mate xs = V.replicateM population (step xs)
  where
    step :: RandomGen g => StringVector -> State g (Vector Char)
    step xs =
      let mom = liftM (xs !) (wrandomRSt population)
          dad = liftM (xs !) (wrandomRSt population)
          split = randomRSt (0, V.length target - 1)
      in do
        m <- mom
        d <- dad
        s <- split
        return (V.take s m V.++ V.drop s d)

elite = population `div` 10
elitism :: (RandomGen g) => StringVector -> State g StringVector
elitism xs = let
  a = V.take (elite) xs
  children = (V.take (population - elite)) `fmap` mate xs
  in do
    b' <- children >>= mutate
    let xs' = (a V.++ b')
    return xs'


unit_t *mate(unit_t *population)
{
    int i;
    size_t half_population = POPULATION >> 1;
    size_t orig_size = strlen(TARGET);
    int mum, dad, chromosomes;
    char *child;
    char *rest;
    unit_t *children = malloc(sizeof(unit_t) * POPULATION);
    elitism(population, children);
    for(i = ELITE; i < POPULATION; i++)
    {
        mum = rand() % half_population;
        dad = rand() % half_population;
        chromosomes = rand() % orig_size;
        child = malloc(sizeof(char) * (orig_size+1));
        rest = population[dad].text + chromosomes;
        sprintf(child, "%.*s%s", chromosomes, population[mum].text, rest);
        children[i].text = strdup(child);
        children[i].dist = -1;
        if(will_mutate())
            mutate(&children[i], orig_size);
        free(child);
    }
    free_population(population);
    population = children;
    return population;
}

编辑:注意到C版本从同一半中获取父母。编辑了mateCopy来反映这个

2 个答案:

答案 0 :(得分:3)

正如我在评论中指出的那样,当你的人口是同质的时候,你的人口就会趋同,而不是当你对最优秀的个人感到满意时。

您的Haskell版本可能会收敛太快。你的健身功能导致你的人口收敛的速度是“探索”和“剥削”之间的权衡。当人们“探索”时,您可以将其视为在寻找山丘的健身景观中快速移动。 “开拓”包括攀登已经找到的山丘。

如果您的健身功能具有很强的选择性(我假设您的意思是“更好”),那么它正在利用以牺牲的方式进行探索。您在Haskell中编写的解决方案可能过早地消除了它的多样性 - 并且没有突变它不能再创建它。

答案 1 :(得分:0)

更好/更差的伴侣功能是什么意思?

  

haskell版本具有更好的配合功能,但需要   变异甚至收敛

     

C版本没有变异   更糟糕的配偶功能但收敛速度更快

人们可以使用的唯一客观事实是,目前一个版本没有变异而另一个版本没有变异。

如果我们谈论GA,所使用的语言是无关紧要的,如果我们谈论性能并且实现具有可比性(即您使用两种语言或其他类型的数组),它就变得相关。