2017-08-14 41 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

相關問題