我怎样才能让我的世代价值为我的遗传算法工作Python

问题描述

我创建了一种遗传算法,通过遵循此视频中的代码https://www.youtube.com/watch?v=nhT56blfRpE

不幸的是,我无法让几代部分工作,并且结果中有 0 代,即使它创建了一个在运行中每次都不同的解决方案。 我拥有的变量是名称、健康度和体重,它可以转换为节目名称、评分和剧集数。 至于代码生成部分将无法工作,也不会考虑程序创建最佳解决方案所需的时间。

from random import choices,randint,randrange,random #allows for a random creations to be made
from typing import List,Callable,Tuple #allows to use list functions and callable
from collections import namedtuple #allows the use for named tuple
import time #allows for time to be used
from functools import partial #allows for partial to be used

Genome=List[int]
Population=List[Genome]
fitnessFunc=Callable[[Genome],int] #puts the fitness with the genome
PopulateFunc=Callable[[],Population]#gives out new solutions
SelectionFunc=Callable[[Population,fitnessFunc],Tuple[Genome,Genome]] #takes the population and fitness to make select for a new solution
CrossoverFunc=Callable[[Genome,Genome],Genome]]#takes two genomes and produces two genomes
MutationFunc=Callable[[Genome],Genome]#takes a genome and sometimes produces a new genome
PrinterFunc=Callable[[Population,int,None]
#puts the functions into perameters 
Thing=namedtuple('Thing',['name','value','weight'])#gives a structure for the list below

things=[
    Thing('Seishun Buta Yarou wa Bunny Girl Senpai no Yume wo Minai (TV)',8.38,13),Thing('Bakemonogatari',8.36,15),Thing('Kodomo no Jikan (TV)',6.82,12),Thing('Tsuki ga Kirei',8.18,Thing('Tokyo Ravens',7.53,24),Thing('Kono Subarashii Sekai ni Shukufuku wo!',8.15,10),Thing('Shingeki no Kyojin S4',9.2,Thing('Dr Stone S2',8.33,11),Thing('The Promised Neverland',8.11,Thing('Re:Zero',8.7,Thing('Toradora!',8.24,25),Thing('Sousei no Onmyouji',7.33,50),Thing('Rurouni Kenshin: Meiji Kenkaku Romantan',8.31,94),Thing('FullMetal Alchemist: brotherhood',64),Thing('Steins;Gate',9.11,Thing('Boku no Pico',4.31,3)
]

def generate_genome(length: int) -> Genome:
    return choices([0,1],k=length)
    #creates a random genome 
##genome

def generate_population(size: int,genome_length: int) -> Population:
    return [generate_genome(genome_length)for _ in range(size)]
    #generates a list of genomes
##population

def fitness(genome: Genome,things: things,weight_limit: int) -> int:
    if len(genome)!=len(things):#checks if the two are the same length
        raise ValueError("genome and things must be of the same length")

    weight=0
    value=0

    for i,thing in enumerate (things):
        if genome[i]==1:
            weight+=thing.weight
            value+=thing.value #adds the weight values and fitness values
            
            if weight>weight_limit: #checks if the genome is under the weight limit
                return 0
    return value
#fitness function

def selection_pair(population: Population,fitness_func: fitnessFunc) -> Population:
    return choices(
        population=population,weights=[fitness_func(genome) for genome in population],k=2 #draw twice (two parents)
    )
#selection function

def single_point_crossover(a: Genome,b: Genome) -> Tuple[Genome,Genome]:
    if len(a)!=len(b):
        raise ValueError("Genomes a and b must be of same length")
    #checks the size of the genomes that was selected
    #the genomes have to be the same in length or it will not work

    length=len(a)
    if length<2:#checks the length as they would have to be at least 2 or else you are unable to cut them in half
        return a,b

    p=randint(1,length-1)#randomly takes parts of the two genomes
    return a[0:p]+b[p:],b[0:p]+a[p:]#and then creates them
#crossover function

def mutation(genome: Genome,num: int=1,probability: float=0.5) -> Genome:
    for _ in range(num):
        index=randrange(len(genome))#if the number made is higher than the probability it is left alone
        genome[index]=genome[index] if random() > probability else abs(genome[index]-1)#makes the absolute value so that it is either 1 or 0
        #people hatee this one but why ^^
    return genome
#mutation function

def run_evolution(
    populate_func: PopulateFunc,fitness_func: fitnessFunc,#for these two it populates with variables
    fitness_limit: int,#if the fitness limit is reached then the program is done
    selection_func: SelectionFunc=selection_pair,crossover_func: CrossoverFunc=single_point_crossover,mutation_func: MutationFunc=mutation,# initialises the above three
    generation_limit: int=100 #limits on how much is made
) -> Tuple[Population,int]:
    population=populate_func()#populates with random genomes

    for generations in range(generation_limit):
        population=sorted(
            population,key=lambda genome: fitness_func(genome),reverse=True
            #sorts the fitness of the genomes so that the higher number is at the start
        )
        if fitness_func(population[0])>=fitness_limit:
            break
        #if the fitness limit is reached then the program ends (breaks)

        next_generation=population[0:2]#gets the top two genomes for the next generation

        for j in range(int(len(population)/2)-1):
            parents=selection_func(population,fitness_func) #gets the top two to be made as parents
            offspring_a,offspring_b=crossover_func(parents[0],parents[1]) #puts the genomes into seprate variables
            offspring_a=mutation_func(offspring_a)
            offspring_b=mutation_func(offspring_b)#creates a mutation for both genomes randomly
            next_generation+=[offspring_a,offspring_b]
            #creates the next genomes and puts them into population
        population=next_generation

    population=sorted(
         population,reverse=True
       #sorts the fitness of the genomes so that the higher number is at the start
    )

    return population,generations
#evolutionary main loop

def genome_to_things(genome: Genome,things: things) -> things:
    result=[]
    for i,thing in enumerate(things):
        if genome[i]==1:
            result+=[thing.name]

    return result 
#collects the best solutions so that it may be printied out for later  


start=time.time()
population,generations=run_evolution(
    populate_func=partial(
        generate_population,size=10,genome_length=len(things)
    ),fitness_func=partial(
        fitness,things=things,weight_limit=94
    ),fitness_limit=10,generation_limit=100
)
end=time.time()
#runs the program

print(f"number of generations: {generations}")
print(f"time: {end-start}s")
print(f"best solution: {genome_to_things(population[0],things)}")
#prints results

解决方法

这意味着你的 GA 在第一代初始 0 中直接找到了满足你的适应度极限的解。

如果我使用 fitness_limit=100 运行您的代码,我会得到以下输出。

number of generations: 99
time: 0.014637947082519531s
best solution: ['Seishun Buta Yarou wa Bunny Girl Senpai no Yume wo Minai (TV)','Bakemonogatari','Tsuki ga Kirei','Kono Subarashii Sekai ni Shukufuku wo!','Shingeki no Kyojin S4','Dr Stone S2','Re:Zero','Boku no Pico']