遗传算法字符串猜测

时间:2017-11-27 07:36:10

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

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

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

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

1 个答案:

答案 0 :(得分: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通常在没有修复的情况下不会收敛),此修复尤其重要。