2017-08-14 37 views
1
from fuzzywuzzy import fuzz 
import random 
import string 

chars = ["T1", "T2", "T3", "T4", "T5", "T6", "T7", "T8", "T9", "R1", "R2", "N1", 
     "N2", "G1", "G2", "G3", "H1", "H2", "H3", "K1", "K2", "K3", "K4", "D1", 
     "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9"] 

class Agent: 

    def __init__(self, length): 
     self.string = ' '.join(random.choice(chars) for _ in xrange(length)) 
     self.fitness = -1 

    def __str__(self): 
     return 'String: ' + str(self.string) + ' Fitness: ' + str(self.fitness) 

in_str = None 
in_str_len = None 
population = 20 
generations = 10000 

def ga(): 
    agents = init_agents(population, in_str_len) 

    for generation in xrange(generations): 
     print 'Generation: ' + str(generation) 

     agents = fitness(agents) 
     agents = selection(agents) 
     agents = crossover(agents) 
     agents = mutation(agents) 

     if any(agent.fitness >= 100 for agent in agents): 
      print 'Threshold met!' 
      exit(0) 

def init_agents(population, length): 
    return [Agent(length) for _ in xrange(population)] 

def fitness(agents): 
    for agent in agents: 
     agent.fitness = fuzz.ratio(agent.string, in_str) 
    return agents 

def selection(agents): 
    agents = sorted(agents, key=lambda agent: agent.fitness, reverse=True) 
    print '\n'.join(map(str, agents)) 
    agents = agents[:int(0.2 * len(agents))] 
    return agents 

def crossover(agents): 
    offspring = [] 

    for _ in xrange((population - len(agents))/2): 
     parent1 = random.choice(agents) 
     parent2 = random.choice(agents) 
     child1 = Agent(in_str_len) 
     child2 = Agent(in_str_len) 
     split = random.randint(0, in_str_len) 
     child1.string = parent1.string[0:split] + parent2.string[split:in_str_len] 
     child2.string = parent2.string[0:split] + parent1.string[split:in_str_len] 

     offspring.append(child1) 
     offspring.append(child2) 

    agents.extend(offspring) 
    return agents 


def mutation(agents): 
    for agent in agents: 
     for idx, param in enumerate(agent.string): 
      if random.uniform(0.0, 1.0) <= 0.1: 
       agent.string = agent.string[0:idx] + random.choice(chars) + agent.string[idx+1:in_str_len] 
    return agents 


if __name__ == '__main__': 
    in_str = 'T1T3N1N2H1H2' 
    in_str_len = len(in_str) 
    ga() 

在此代码中,我使用交叉函数和mutate函数来开发更强大的群体。 但是,我需要这些功能来将输入字符作为一个整体单元处理。 例如: 虽然变异,函数替换1或T从'T1'或K或1从'K1' 我需要它将T1和K1作为一个单元而不是字符串,并将它们替换为整体与其他单位如T2,T3,T4等 任何建议或提示,将不胜感激。 感谢你。如何在python中将多个字符定义为单个组?

+0

请创建一个[mcve]。 –

+0

如果你想要这样的行为,你可能不应该在'mutation()'中遍历'agent.string':它将遍历字符串的单个字符。 – Evert

+1

也许更好,不要在初始化时连接各个“基因”,但将它们保存为列表。像'self.string = [random.choice(chars)for _ in xrange(length)]''。也许可以将'string'重命名为'chromosome',并相应地调整其余的代码。 – Evert

回答

0

正如@Evert和@ user3080953所提到的那样,问题在于您对染色体的表示。

你的遗传算法的目标是什么?

从您提供的代码中,您似乎希望群体朝目标字符串in_str发展。但是,正如你在你的问题中提到的那样,一些字符串是无效的,因此不应该被考虑(例如T1D8是有效染色体,而T18D不是)。

有几个解决方案,这可能会解决这个问题:

方法#1(由@ user3080953提供)

交叉过程中明智地选择你的分割点。由于每个有效基因长度为2字符串,因此拆分索引必须是偶数。因此,而不是:

split = random.randint(0, in_str_len) 

...你可以写:

split = 2 * random.randint(0, in_str_len // 2) 

然后,使在mutation()功能类似的变化。

方法#2(如通过@Evert提供)

编码每个染色体作为字符串的列表,而不是一个单一的字符串。

所以,变化:

self.string = ' '.join(random.choice(chars) for _ in xrange(length)) 

...到:

self.string = [random.choice(chars) for _ in xrange(length)] 

方法3

这可能是矫枉过正,但如果你希望有更好的可扩展性,你应该考虑将每个基因包装成一个物体。例如:

class Gene: 
    # not literally, but you get the picture hopefully 
    Gene._gene_set = set([...]) 

    def __init__(self, gene_str): 
     formatted_gene_str = self._format(gene_str) 
     assert self._is_valid_gene_str(formatted_gene_str) 

     self._gene_str = formatted_gene_str 

    def _is_valid_gene_str(self, gene_str): 
     return gene_str in Gene._gene_set 

    def _format(self, gene_str): 
     return gene_str.strip().upper()  

    def __str__(self): 
     return self._gene_str 

    def __eq__(self, other): 
     return type(other) is Gene and str(self) == str(other) 

此外,您的代码似乎存在潜在的设计缺陷。你写道:

self.string = ' '.join(random.choice(chars) for _ in xrange(length)) 

......在每个基因之间插入一个空格...;但是,目标字符串没有空格。 fuzz.ratio()使用Levenshtein Distance来计算序列之间的差异。

+1

谢谢你的详细解释。你已经清除了我的疑惑。 – BladeSama

+0

关于这个缺陷......你能提出任何其他的方法来避开这个问题吗? – BladeSama

+0

@BladeSama如果你选择方法#1,只需通过空字符串'''.join加入(...而不是空格)'''.join(...'。基本上,你只需要将目标染色体的相同表示的对象。 – ljeabmreosn

相关问题