【问题标题】:genetic algorithm string guess遗传算法字符串猜测
【发布时间】:2018-05-10 09:25:55
【问题描述】:

我正在尝试了解如何实现遗传算法并编写了一个简单的字符串猜测。我无法理解为什么此解决方案不起作用。

我相信我的问题在于我的新一代人口?最新一代似乎没有改善健康价值。我也不确定我是否正确地进行了交叉和突变率。任何帮助将不胜感激!

POP_SIZE = 300;
CROSSOVER_RATE = 0.7;
MUTATION_RATE = 0.01
GENESET = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"
target = "Hello World"
RAND_NUM = random.random()

def generateBasePopulation(population_size):
    population = dict()

    for _ in range(POP_SIZE):
        gene = generateParent(len(target))
        population[gene] = 0

    return population


def generateNewPopulation(population, population_size):
    newPopulation = dict()

    while(len(newPopulation) <= POP_SIZE):
        child_one, child_two = crossover(child_one, child_two)
        child_one = mutate(child_one)
        child_two = mutate(child_two)


    newPopulation[child] = 0
    newPopulation[child_two] = 0
    return newPopulation



def assignFitness(population):
    for x in population:
        population[x] = getFitness(x)


def generateParent(length):
    genes = list("")
    for i in range(0,length):
        random_gene = random.choice(GENESET)
        genes.append(random_gene)
    return(''.join(genes))

def getFitness(candidate):
    fitness = 0
    for i in range(0, len(candidate) - 1):
        if target[i] == candidate[i]:
            fitness += 1
    return(fitness)

def mutate(parent):
    gene_index_to_mutate = random.randint(0, len(parent) - 1)
    mutation_value = random.choice(GENESET)
    genes = list(parent)
    genes[gene_index_to_mutate] = mutation_value
    return(''.join(genes))

def crossover(parentA, parentB):
    if(RAND_NUM < CROSSOVER_RATE):
        random_index = random.randint(0, len(target))
        parentASlice = parentA[:random_index]
        parentBSlice = parentB[random_index:]

        return (parentASlice + parentBSlice), (parentBSlice + parentASlice)
    return parentA, parentB


def chooseChild(population):
    fitnessSum = sum(population.values())
    pick = random.uniform(0, fitnessSum)
    current = 0
    for pop in population:
        current += population[pop]
        if current >= pick:
            return pop


def main():
    population = generateBasePopulation(POP_SIZE)

    targetNotFound = True

    while(targetNotFound):
        assignFitness(population)
        if target in population:
            print("target found!")
            targetNotFound = False
        if(targetNotFound):
            tempPopulation = generateNewPopulation(population, POP_SIZE)
            population.clear()
            population = tempPopulation

【问题讨论】:

    标签: python python-3.x genetic-algorithm evolutionary-algorithm


    【解决方案1】:

    generateNewPopulation 函数存在一些问题。

    child_onechild_two 在赋值之前被引用

    您需要人口中的两个人来执行交叉。有几种选择算法,但只是给出一个想法,您可以从tournament selection 的形式开始:

    def extractFromPopulation(population):
        best = random.choice(list(population.keys()))
    
        for _ in range(4):
            gene = random.choice(list(population.keys()))
            if population[gene] > population[best]:
                best = gene
    
        return best
    

    这里选择压力(range(4))是固定的。这是您在实际案例中必须调整的参数之一。

    现在我们有了:

    def generateNewPopulation(population, population_size):
        newPopulation = dict()
    
        while len(newPopulation) <= POP_SIZE:
            child_one = extractFromPopulation(population)
            child_two = extractFromPopulation(population)
    
        # ...
    

    代码仍然不起作用,因为

    newPopulation 中没有插入新的个人

    只需缩进两行:

    newPopulation[child] = 0
    newPopulation[child_two] = 0
    

    (它们必须是while 循环的一部分)

    修改后的generateNewPopulation函数如下:

    def generateNewPopulation(population, population_size):
        newPopulation = dict()
    
        while len(newPopulation) <= POP_SIZE:
            child_one = extractFromPopulation(population)
            child_two = extractFromPopulation(population)
    
            child_one, child_two = crossover(child_one, child_two)
            child_one = mutate(child_one)
            child_two = mutate(child_two)
    
            newPopulation[child_one] = 0
            newPopulation[child_two] = 0
    
        return newPopulation
    

    crossover 函数不能基于固定的RAND_NUM

    删除RAND_NUM = random.random() 分配并更改crossover 函数以在每次调用时使用新的随机值:

    def crossover(parentA, parentB):
        if random.random() < CROSSOVER_RATE:
            random_index = random.randint(0, len(target))
            parentASlice = parentA[:random_index]
            parentBSlice = parentB[random_index:]
    
            return (parentASlice + parentBSlice), (parentBSlice + parentASlice)
    
        return parentA, parentB
    

    由于没有保留第二个父项的模式,因此代码也无法正确执行单点交叉。


    您可以更改许多细节以提高性能,但作为一个开始的示例,它可能就足够了(......它有效)。

    找到解决方案的平均世代数约为 158200 运行的平均值)。


    编辑(感谢 alexis 提供comment

    MUTATION_RATE 未使用,并且总是发生突变。 mutate 函数应该类似于:

    def mutate(parent):
        if random.random() < MUTATION_RATE: 
            gene_index_to_mutate = random.randint(0, len(parent) - 1)
            mutation_value = random.choice(GENESET)
            genes = list(parent)
            genes[gene_index_to_mutate] = mutation_value
            return ''.join(genes)
    
        return parent
    

    如果您保留轮盘赌选择算法(chooseChild 在没有修复的情况下通常不会收敛),此修复尤为重要。

    【讨论】:

    • 我相信在 OP 的代码中,孩子们应该使用 chooseChild() 来选择,它已定义但未使用(并且选择与适应度成正比的概率,这将推动改进)。
    • @alexis 你说得对,我错过了那个功能。可能在 OP 的代码中,孩子们应该用chooseChild() 来选择。无论如何,建议的更改应该仍然有效。
    • @alexis 感谢您的评论,存在另一个问题(与mutation() 过程有关),但它仅在尝试chooseChild() 函数时出现。
    猜你喜欢
    • 1970-01-01
    • 2013-04-18
    • 2011-01-11
    • 1970-01-01
    • 2012-07-07
    • 2011-07-22
    • 1970-01-01
    • 2011-08-03
    • 1970-01-01
    相关资源
    最近更新 更多