使用Python中的遗传学/进化论为波斯游戏选择设备

如何在自己喜欢的游戏中选择最好的设备?当然,您可以随意整理所有可能的组合(例如,《魔兽世界》中的强盗)并找到最佳组合。没有任何魔术或机器学习。但是,有可能在不尝试每种组合的情况下,借助遗传算法而不是“直接”实现此结果吗?知道强盗是如何繁殖和进化的,这很有趣。走。

图片

这篇文章是在加深以往的话题,已经描述了人物的实现的细节和设备的影响。

目的:向有兴趣的人展示如何独自创建虚拟的游戏代理群体,将他们推到一起,实现简单的遗传算法(带有突变),并使用HTML,CSS和Javascript可视化所有这些。

您需要:

1. Python 3 +安装matplotlib模块IDE(我有PyCharm);
2.如果要编辑交互式报告模板,则对HTML,CSS,JavaScript(jQuery)有基本的了解。

步骤1-设定目标,应对OOP


因此,我们编码的最终目标是通过遗传学/进化/自然选择的模拟获得最佳服装。并有机会直观地跟踪此路径。

我们将目标转变为特定的任务:

  1. 在个人层面上思考和实施遗传机制(强盗的“基因”应直接影响设备的选择)
  2. 实施后代的出生机制,基因向他们的转移以及随机的微小突变,以确保变异性和找到最佳后代的可能性
  3. 实现遗传的“竞争”,基因的选择(将其携带者融合在一起,使碰撞的结果推翻“不良”基因并提升“良好”基因)
  4. 将所有东西组装到一个连贯的系统中,强盗可以演变成理想的战斗机
  5. 收集和可视化数据,以便让您佩服(否则很有趣!)
  6. 评估这些活动的有用性

在这里,OOP将帮助我们,我们将创建4个基本类:

图片

第2步-加强Rogue类的繁殖


同意在岸上的条款


在本文中,我将分开使用术语“基因”(在代码中- 基因)和“基因型”(在代码中- 基因型)。也将存在“突变”(在代码中为mutatemutation),这意味着一个或多个含义相同的基因发生了变化。繁殖将仅通过从父母那里“萌芽”后代而发生,因此不会发生交叉和其他类似的并发症。强盗的基因型是由他的基因组成的7个数字清单

图片

因此,与上次相比,“流氓”阶级正在显着增长。

1。强盗以一种可能的方式接收基因(在第一天产生或从“父母”那里继承而来,无论有无突变)。为此方法generate_random_genesmutate_genesmutate_gene负责

编码基因形成方法
    #     ():
    def generate_random_genes(self):
        dbg = DBG_rogue_generate_random_genes

        self.my_genes[0] = randrange(0, len(RIGHT_HANDS))    # <--   :
        self.my_genes[1] = randrange(0, len(LEFT_HANDS))     # <--   :
        self.my_genes[2] = randrange(0, len(GLOVES))         # <--  :
        self.my_genes[3] = randrange(0, len(HEADS))          # <--  :
        self.my_genes[4] = randrange(0, len(CHESTS))         # <--  :
        self.my_genes[5] = randrange(0, len(PANTS))          # <--  :
        self.my_genes[6] = randrange(0, len(BOOTS))          # <--  :

        if dbg:  #  :
            print('\nf "generate_random_genes":' + '\n\tgenes generated:\n\t', end='')
            print(self.my_genes)


    #      :
    def mutate_genes(self, parent_genes):
        dbg = DBG_rogue_mutate_genes

        #     :
        self.my_genes = parent_genes.copy()

        #    :
        event_mutation = randint(1, 10)

        #     ,    :
        if event_mutation == 10:
            if dbg:  #  :
                print('\nf "mutate_genes"   :' + '\n\t  \n\told genes: ', end='')
                print(parent_genes)
                print('\tnew genes: ', end='')
                print(self.my_genes)
            return 0

        #    :
        else:
            #  ""  =  ,   :
            mutation_volume = randint(0, 30)
            mutation_iters = 1
            if 22 <= mutation_volume <= 28:
                mutation_iters = 2
            elif 29 <= mutation_volume <= 30:
                mutation_iters = 3

            if dbg:  #  :
                print('\nf "mutate_genes" :' + '\n\t : ' + str(mutation_iters))

            #  ,   :
            genes_available = [0, 1, 2, 3, 4, 5, 6]

            #   :
            genes_mutated = []

            current_iter = 0
            while current_iter < mutation_iters:
                if dbg:  #  :
                    print('\tw1')

                #     :
                gene_with_forced_mutation = choice(genes_available)

                #      :
                if gene_with_forced_mutation not in genes_mutated:
                    self.mutate_gene(gene_with_forced_mutation)
                    genes_mutated.append(gene_with_forced_mutation)
                    current_iter += 1
                    if dbg:  #  :
                        print('\tcurrent_iter =', current_iter)
                else:
                    if dbg:  #  :
                        print('\telse, because ' + str(gene_with_forced_mutation) + ' already in genes_mutated')

        if dbg:  #  :
            genes_mutated_str = ''
            if len(genes_mutated) > 1:
                for x in genes_mutated:
                    genes_mutated_str += str(x) + ', '
            else:
                genes_mutated_str = str(genes_mutated[0])
            print('\nf "mutate_genes" :' + '\n\told genes: ', end='')
            print(parent_genes)
            print('\tgenes_mutated: ' + genes_mutated_str)
            print('\tnew genes: ', end='')
            print(self.my_genes)


    #     ,      :
    def mutate_gene(self, gene_id):
        dbg = DBG_rogue_mutate_gene

        current_value = self.my_genes[gene_id]
        new_value = current_value

        if dbg:  #  :
            print('\nf "mutate_gene":' + '\n\tgene_id: ' + str(gene_id) + '\n\told gene value: ' + str(current_value))

        tries = 0
        while new_value == current_value:
            if dbg and tries > 0:  #  :
                print('\tw2, because ' + str(new_value) + ' = ' + str(current_value) )
            new_value = randrange(0, len(LINKS_TO_EQUIP_DICTS[gene_id]))
            self.my_genes[gene_id] = new_value
            tries += 1

        if dbg:  #  :
            print('\tnew gene value: ' + str(new_value) + '\n\ttries: ' + str(tries))


2.基因(基因型)决定哪个齿轮磨损强盗(立即在出生初始化对象处)。为此,调用apply_genes方法

apply_genes
    # ""  ()     :
    def apply_genes(self):
        dbg = DBG_rogue_apply_genes

        pointer = 0
        for item_id in self.my_genes:
            self.wear_item(pointer, item_id, LINKS_TO_EQUIP_DICTS[pointer])
            pointer += 1

        if dbg:  #  :
            print('\nf "apply_genes":' + '\n\t.')
            print(self)


3.设备将确定强盗“定级”的最终值。为此,将调用compute_rate方法,该方法计算损坏的数学期望:

计算率
    #     :
    def calculate_rate(self):
        dbg = DBG_rogue_calculate_rate

        #   :
        p_hit = self.stat_hit / 100

        #      :
        p_glancing = self.stat_glancing_percent / 100
        not_p_glancing = 1 - self.stat_glancing_percent / 100

        #      :
        p_crit = self.stat_crit / 100
        not_p_crit = 1 - self.stat_crit / 100

        #   :
        expectation_modificator = p_hit * (p_glancing * 0.7 + not_p_glancing * (p_crit * 2 + not_p_crit))

        #      :
        expectation_damage = expectation_modificator * self.stat_attackpower
        expectation_damage = round(expectation_damage, 3)

        if dbg:
            print('\t  =', expectation_modificator)
            print('\t  =', expectation_damage)

        return expectation_damage


4.强盗等级将成为导致繁殖或可耻死亡的决定因素。为此,引入了“胜利”(embody_win方法)和“失败”(embody_defeat方法)的概念:

embody_win和embody_defeat
    #    :
    def embody_win(self):
        dbg = DBG_rogue_embody_win

        self.my_wins += 1
        stats.genes_add_win(self.my_genes)

        #    :
        if self.my_wins % population.wins_to_reproduce == 0:

            #    :
            total_borns = choice(population.possible_birth_quantities)
            if dbg:
                print('  ' + str(total_borns))

            for x in range(0, total_borns):
                if dbg:
                    print(self.name + '  ...')

                #  -:
                new_rogue = Rogue(self.my_genes, self.my_generation, from_parent=True)
                ROGUES_LIST.append(new_rogue)

            Population.day_of_last_changes = current_day

        #         :
        if self.my_wins > Population.record_max_wins:
            Population.record_max_wins = self.my_wins
            Population.max_winner_name = self.name
            Population.max_winner_genes = self.my_genes


    #    :
    def embody_defeat(self):
        dbg = DBG_rogue_embody_defeat

        self.my_defeats += 1

        #    :
        if self.my_defeats == population.defeats_to_die:
            self.alive = False
            Population.how_many_rogues_alive -= 1
            Population.day_of_last_changes = current_day

            if dbg:
                print(self.name + ' ...')


好了,已经对类构造函数进行了相应的重新设计,以使此链“基因-设备-评级”起作用:

流氓类构造函数
    def __init__(self, genes_list_inherited, parent_generation, from_parent=True, genes_can_mutate=True):

        #    ,    id  :
        # 0 -  , 1 -  , 2 - , 3 - , 4 - , 5 - , 6 - 
        self.equipment_slots = [0] * 7

        #    ,      :
        self.equipment_names = [''] * 7

        #    ( -     ):
        self.basic_stat_agility = 50
        self.basic_stat_power = 40
        self.basic_stat_hit = 80
        self.basic_stat_crit = 20
        self.basic_stat_mastery = 0

        #     :
        self.set_stats_without_equip()

        #  :
        Population.how_many_rogues += 1
        Population.how_many_rogues_alive += 1

        #  :
        self.my_generation = parent_generation + 1
        if self.my_generation > Population.generations:
            Population.generations = self.my_generation

        # "" :
        self.name = '"' + str(Population.how_many_rogues) + '-,   ' + str(parent_generation + 1) + '"'

        #  :
        self.alive = True

        #    :
        self.my_wins = 0
        self.my_defeats = 0

        #   :
        self.my_genes = [0] * 7

        if genes_can_mutate:
            #      ,     :
            if from_parent:
                self.mutate_genes(genes_list_inherited)
            else:
                self.generate_random_genes()
        else:
            self.my_genes = genes_list_inherited

        #     :
        stats.genes_add_presence(self.my_genes, self.my_generation)

        #     :
        self.apply_genes()


用图片总结此类逻辑:

图片

第三阶段-人口,统计数据和挑战等级


只有三件事需要人口”

  1. 从具有一定数量的随机基因和生物学参数的强盗中创建一个种群,这些种群将始终保持不变(wins_to_reproduce-一个强盗需要获得多少胜利才能繁殖,failures_to_die-多少次失败会使一个人丧生);
  2. 人口的“重置”,即 当前阶段的最后一天结束时,所有强盗都将被销毁,并创建具有最佳基因型的强盗(请参阅reload方法);
  3. 存储一些人口统计数据。

阶级人口
class Population():
    """     ."""

    how_many_rogues = 0 # <--   
    how_many_rogues_alive = 0 # <--   
    how_many_battles = 0 # <--  
    how_many_ties = 0 # <--  
    generations = 0 # <--  
    day_of_last_changes = 0  # <--       
    max_days_without_changes = 0  # <--    

    #        ,      :
    record_max_wins = 0
    max_winner_name = 'none'
    max_winner_genes = 'none'


    #       :
    def __init__(self, total, possible_birth_quantities, wins_to_reproduce, defeats_to_die):

        #   :
        self.initial_size = total

        #   ,        :
        self.wins_to_reproduce = wins_to_reproduce
        self.defeats_to_die = defeats_to_die
        self.possible_birth_quantities = possible_birth_quantities

        while total > 0:

            #   ,  " "    ,     :
            new_rogue = Rogue('', 0, from_parent=False)

            #   :
            ROGUES_LIST.append(new_rogue)

            total -= 1


    #     :
    def __str__(self):
        text = ':\n'
        text += ': ' + str(Population.generations) + '\n'
        text += ' : ' + str(Population.how_many_rogues) + '\n'
        text += ' : ' + str(Population.how_many_rogues_alive) + '\n'
        text += ' : ' + str(Population.record_max_wins) + '\n'
        text += ' : ' + str(Population.max_winner_name) + '\n'
        text += ' : ' + str(Population.max_winner_genes) + '\n'
        return text


    #  ,    how_many_to_save     :
    def reload(self, how_many_to_save):

        #  - :
        Population.how_many_rogues_alive = 0

        # ""   :
        for x in ROGUES_LIST:
            x.alive = False

        #        :
        list_genotypes_top = stats.get_ordered_list_from_dict(LIST_FOR_DICTS_GENOTYPES[current_stage], inner_index=2)

        #      :
        for x in range(0, how_many_to_save):

            #      :
            genotype_in_str = list_genotypes_top[x][0]
            genotype_in_list = []
            for char in genotype_in_str:
                if char != '-':
                    genotype_in_list.append( int(char) )

            #   ,      ,     :
            new_rogue = Rogue(genotype_in_list, 0, from_parent=True, genes_can_mutate=False)

            #   :
            ROGUES_LIST.append(new_rogue)


Stats执行两个主要任务:

  1. 模拟过程中的数据收集(例如,每天的指标:活着的强盗数量,独特基因型的数量等)
  2. ( matplotlib, HTML-, ).

展望未来,我将展示该报告的外观:github上提供

图片

了该报告的HTML模板代码 还可以在github上更好地查看Stats类(否则,这会伤害很多行)。挑战者负责模拟随机选择的强盗之间的冲突。每天,该方法称为perform_battles,它是由活着的强盗决斗对形成的,并以perform_battle方法面对它们,最终,对于每个强盗,都会导致embody_win方法,任何embody_defeat方法



顺便说一句,如果事实证明发生过抽奖(基因型,因此强盗的等级是相同的),则它们会发散而不会产生后果:

阶级挑战者
class Challenger():
    """      ."""

    #   :
    def perform_battles(self):
        dbg = DBG_challenger_perform_battles

        #    :
        rogues_alive = []
        for x in ROGUES_LIST:
            if x.alive:
                rogues_alive.append(x)

        #  :
        shuffle(rogues_alive)

        #       :
        pairs_total = int(len(rogues_alive) // 2)

        if dbg:
            print('pairs_total =', pairs_total)

        #       :
        counter = 0
        pointer = 0
        while counter < pairs_total:
            a_1 = rogues_alive[pointer]
            a_2 = rogues_alive[pointer + 1]
            self.perform_battle(a_1, a_2)
            counter += 1
            pointer += 2


    #     :
    def perform_battle(self, rogue_1, rogue_2):
        dbg = DBG_challenger_perform_battle

        if dbg:
            print('\n  :', rogue_1.name, '', rogue_2.name)

        #     (        ):
        rating_1 = rogue_1.calculate_rate()
        rating_2 = rogue_2.calculate_rate()

        #  :
        Population.how_many_battles += 1

        if dbg:
            print('\t :', rating_1, '', rating_2, '.')

        #      :
        if rating_1 > rating_2:
            rogue_1.embody_win()
            rogue_2.embody_defeat()
        elif rating_1 < rating_2:
            rogue_1.embody_defeat()
            rogue_2.embody_win()
        else:
            Population.how_many_ties += 1
            if dbg:
                print('\t !  !')


好了,现在我们来看程序的主体,该主体将代码的所有上述部分链接在一起。首先是确定模拟关键参数的常数:阶段数,一个阶段中的天数,人口中的强盗初始数量等。然后是用于调试的辅助常数。挑战本身:

运行模拟的代码
# :
MAX_STAGES = 8 # <--      
MAX_DAYS_AT_STAGE = 20 # <--        
SLIDING_FREQUENCY = 10 # <--     HTML-    (1 =   , 10 =   10 )
ROGUES_AT_BEGIN = 10 # <--    (  )
WINS_TO_REPRODUCE = 2 # <--     ,  
DEFEATS_TO_DIE = 2 # <--      
POSSIBLE_BIRTH_QUANTITIES = [1] # <--   ,       , :
# [1, 2] ,   50%-    1,  1 
# [1, 1, 2] ,   66%-   1 

HTML_GSQUARE_SIDE = 10 # <--   ,     
HTML_GSQUARE_MARGIN = 3 # <--   ,     

#     :
LINKS_TO_EQUIP_DICTS = [RIGHT_HANDS, LEFT_HANDS, GLOVES, HEADS, CHESTS, PANTS, BOOTS]

#         (    ):
LIST_FOR_DICTS_GENOTYPES = [{}] * MAX_STAGES

#        :
DICT_UNIQUE_GENOTYPES = {}

#      :
DICT_DAYS = {}

# ,       -  :
ROGUES_LIST = list()

#       (     )
current_stage = 0

#     "" (    /):
DBG_rogue_mutate_genes = False
DBG_rogue_generate_random_genes = False
DBG_rogue_apply_genes = False
DBG_rogue_calculate_rate = False
DBG_rogue_mutate_gene = False
DBG_rogue_embody_win = False
DBG_rogue_embody_defeat = False
DBG_rogue_wear_item = False
DBG_challenger_perform_battles = False
DBG_challenger_perform_battle = False
DBG_days_report = False  # <--     



# :
if __name__ == '__main__':

    #  :
    time_begin = time()

    #        :
    max_days_for_current_stage = 0
    current_day = 1
    while current_stage < MAX_STAGES:

        #    :
        if current_stage == 0:

            #   :
            stats = Stats()

            #          ,      :
            population = Population(ROGUES_AT_BEGIN, POSSIBLE_BIRTH_QUANTITIES, wins_to_reproduce=WINS_TO_REPRODUCE, defeats_to_die=DEFEATS_TO_DIE)

            #     :
            challenger = Challenger()

            # "" :
            print(population)

        #        :
        max_days_for_current_stage += MAX_DAYS_AT_STAGE

        # /      (1  = 1    (*) )
        # * -     -    , -           
        while current_day <= max_days_for_current_stage:
            print('st ' + str(current_stage) + ', day ' + str(current_day))
            if DBG_days_report:
                print('\n\n/DAY', current_day)

            #   :
            challenger.perform_battles()

            if DBG_days_report:
                print('\n', current_day, '.')
                print(population)

            #    :
            stats.add_new_day(current_day)

            #        :
            if current_day - Population.day_of_last_changes > Population.max_days_without_changes:
                Population.max_days_without_changes = current_day - Population.day_of_last_changes

            #    SLIDING_FREQUENCY  (     )     :
            if current_day % SLIDING_FREQUENCY == 0 or current_day == 1 or current_day == MAX_DAYS_AT_STAGE * MAX_STAGES:
                stats.draw_genes_distribution(current_day)

            #    SLIDING_FREQUENCY  (     )     :
            if current_day % SLIDING_FREQUENCY == 0 or current_day == 1 or current_day == MAX_DAYS_AT_STAGE * MAX_STAGES:
                stats.draw_genes_wins(current_day)

            current_day += 1

        #      ,  ,     -  :
        if current_stage < MAX_STAGES - 1:
            population.reload(ROGUES_AT_BEGIN)

        current_stage += 1


    #      ,      LIST_FOR_DICTS_GENOTYPES:
    current_stage -= 1

    #   :
    print('\n:')
    print(DICT_DAYS)

    #       :
    stats.draw_and_put_line_chart_to_file(DICT_DAYS, 1, ' ', '', '', 'charts/chart_population_demography.png')

    #       -  :
    stats.draw_and_put_line_chart_to_file(DICT_DAYS, 0, ' ', '', '', 'charts/chart_population_total.png')

    #      :
    stats.draw_and_put_line_chart_to_file(DICT_DAYS, 2, ' ', '', ' ', 'charts/chart_genotypes_variety.png')

    #      (=   ):
    stats.draw_and_put_line_chart_to_file(DICT_DAYS, 3, ' ', '', '', 'charts/chart_genotypes_ties.png')

    #   HTML   :
    stats.create_index_html()

    #   :
    time_session = time() - time_begin
    duration_info = '  : ' + str(round(time_session, 2)) + ' .'
    print('\n' + duration_info)

else:
    print('__name__ is not "__main__".')


在模拟结束时,将在程序文件夹中生成一个类型名称为“ report 2020-04-25_10-33-54.html”的文件。在本文的下一部分中将有更多关于它的信息。

第4步-可视化数据


为了进一步解释,我将使用这套设备:

evolution_equipment_obvious_strong.py
#       .
#    ,     :
# 0 - , 1 - , 2 - , 3 - , 4 - , 5 - , 6 - 

EQUIPMENT_COLLECTION = 'obvious_strong'

RIGHT_HANDS = dict()
RIGHT_HANDS[1] = (' ', 5000, 0, 0, 0, 0, 0)
RIGHT_HANDS[2] = (' ', 800, 0, 0, 0, 0, 0)
RIGHT_HANDS[3] = (' ', 20, 0, 0, 0, 0, 0)

LEFT_HANDS = dict()
LEFT_HANDS[1] = (' ', 4000, 0, 0, 0, 0, 0)
LEFT_HANDS[2] = (' ', 10, 0, 0, 0, 0, 0)

GLOVES = dict()
GLOVES[1] = (' ', 300, 0, 0, 0, 0, 0)
GLOVES[2] = (' ', 1, 0, 0, 0, 0, 0)

HEADS = dict()
HEADS[1] = (' ', 300, 0, 0, 0, 0, 0)
HEADS[2] = (' ', 1, 0, 0, 0, 0, 0)

CHESTS = dict()
CHESTS[1] = (' ', 300, 0, 0, 0, 0, 0)
CHESTS[2] = (' ', 1, 0, 0, 0, 0, 0)

PANTS = dict()
PANTS[1] = (' ', 300, 0, 0, 0, 0, 0)
PANTS[2] = (' ', 1, 0, 0, 0, 0, 0)

BOOTS = dict()
BOOTS[1] = (' ', 300, 0, 0, 0, 0, 0)
BOOTS[2] = (' ', 1, 0, 0, 0, 0, 0)


这是192(3 * 2 * 2 * 2 * 2 * 2 * 2 * 2)的可能组合因此,将有192种可能的基因型

可视化的主要思想:将可能的基因型的整个空间表示为一个矩形字段,其中每个正方形代表一个单独的基因型。一个简单的算法找到两个数字192的中间除数,分别是16和12:

该算法的代码来自Stats类的构造函数
        #      - :
        list_divisors = list()
        current_number = int(self.genotypes_total // 2)
        while current_number >= 2:
            if self.genotypes_total % current_number == 0:
                list_divisors.append(current_number)
            current_number -= 1
        print('list_divisors:', list_divisors)

        #        :
        somewhere_in_the_middle = len(list_divisors) // 2

        #     :
        side_1 = list_divisors[somewhere_in_the_middle]
        side_2 = self.genotypes_total / side_1

        # ,   ,     ""  :
        self.side_x = int(side_1 if side_1 >= side_2 else side_2)
        self.side_y = int(self.genotypes_total / self.side_x)
        print('side_x =', self.side_x, 'side_y =', self.side_y)


我们得到一个16x12的区域:

图片

此代码生成了可能的基因型列表:

        #    :
        self.list_of_possible_genotypes = list()

        #      :
        for g1 in RIGHT_HANDS:
            #      :
            for g2 in LEFT_HANDS:
                #   :
                for g3 in GLOVES:
                    #   :
                    for g4 in HEADS:
                        #   :
                        for g5 in CHESTS:
                            #   :
                            for g6 in PANTS:
                                #   :
                                for g7 in BOOTS:
                                    current_genotype = str(g1-1)+'-'+str(g2-1)+'-'+str(g3-1)+'-'+str(g4-1)+'-'+str(g5-1)+'-'+str(g6-1)+'-'+str(g7-1)
                                    self.list_of_possible_genotypes.append(current_genotype)

因此,字段上的正方形按此顺序表示基因型:

图片

这是一个重要的功能,因为例如,包含“强剑基因”(蓝色角)的基因型位于字段的上部,而也包含“强匕首基因”的基因型(蓝色角)-占据更高的区域:

图片

因此,最强的基因型(0-0-0-0-0-0-0-0-0)在左上角,而最弱的基因型2-1-1-1- 1-1-1)-相反。这将帮助我们观察情况的动态,因为我们将知道在仿真过程中“主要基因库”应移至字段的左上角:

图片

现在,使用以下参数运行仿真:

MAX_STAGES = 5
MAX_DAYS_AT_STAGE = 40
SLIDING_FREQUENCY = 1
ROGUES_AT_BEGIN = 8
WINS_TO_REPRODUCE = 2
DEFEATS_TO_DIE = 2
POSSIBLE_BIRTH_QUANTITIES = [1]

HTML_GSQUARE_SIDE = 16
HTML_GSQUARE_MARGIN = 3

也就是说,每天要创建8个强盗,每个字段有<div>个带字段的容器(以下称为幻灯片),共40天,分为5个阶段。结果,我们得到了两套各200张的幻灯片:“ 基因型流行 ”(蓝绿色)和“ 基因型胜利 ”(红色和绿色)。正方形的颜色饱和度取决于值。

我们打开生成的HTML报告

图片

,并在第一天和第十天看到这张图片:因此,在第一天,生成了8个强盗,他们的正方形将蒙上阴影直到模拟结束。此外,我们看到他们开始战斗,这意味着出现了最初的胜利,从而导致繁殖。高概率繁殖与突变相关,因此有更多彩色方块。

接下来的几天看看。在第44天,出现了基因型“ 0-0-0-0-0-0-0-0-0 ”(请参见蓝色箭头后面)。在第59天,他已经取得了胜利(参见红色箭头后面的内容)。

图片

在第137天,可以看到“ 0-0-0-0-0-0-0-0-0 ”被人口中出现的人数所击败(参见蓝色箭头的后面)。在最后一天的幻灯片上,他获得了金色的阴影,因为他在胜利数量上名列第一。

图片

如您所见,自然选择确实将种群的“基因库”向左和向上转移,即向更强大的设备转移。 “ 右下 ”基因型比“ 左上 ”基因型弱,因此它们一出现就消失(红色表示没有胜利)。这更清楚地看到b上从更大的角度来看

图片

报告中的结论是正确的:获胜的基因型是0-0-0-0-0-0-0-0-0

图片

使用matplotlib获得图表将有助于进一步评估正在发生的事情:

1.活种群图表显示了动态同时居住的强盗数量的变化;
2.天生的所有人图谱大多接近一条直线,如果您不开始使用人口统计学参数:

图片

3 .“ 基因型多样性图谱-通常先是快速增长,然后放慢速度,逐渐接近最大值。
4.安排“ 画动态”“ -节目的数量如何绘制随着时间的推移(这些是打架时,两个相同基因型无意义的战斗冲突)的变化:

图片

它发生人口”停滞“时,与同基因型劫匪留在它(图显示:一个在急剧增加的数量就种群和独特基因型的数目而言不变性

是不变的种群的停滞意味着计算和绘制幻灯片徒劳无益以减少停滞的时间,有必要减小MAX_DAYS_AT_STAGE常量的值,并且可以看出拥塞有所减少,并且在某些地方完全消失了:

图片

第5步-体验大型基因型空间


现在,让我们尝试对上一篇文章中讨论的evolution_equipment_custom设备集进行仿真,使用稍微不同的参数。此处,值POSSIBLE_BIRTH_QUANTITIES = [1,2]表示,以50%的概率进行繁殖的行为,将生育1个或2个后代。这将增强正在发生的事情的动态性:

MAX_STAGES = 20
MAX_DAYS_AT_STAGE = 50
SLIDING_FREQUENCY = 10
ROGUES_AT_BEGIN = 8 
WINS_TO_REPRODUCE = 2
DEFEATS_TO_DIE = 2
POSSIBLE_BIRTH_QUANTITIES = [1, 2]

HTML_GSQUARE_SIDE = 10
HTML_GSQUARE_MARGIN = 3

此处,幻灯片上基因型分布的模式将有所不同,因为对于总体结果而言最重要的设备(“大师之剑”)现在位于“其他位置”。这套设备的可视化已经通过在设备的功率高于相邻“区域”的不同位置出现多个“电源焦点”来表征。

图片

顺便说一句,该算法明确地确定了设备的最顶层(3-3-0-0-0-0-0-0-1),该设备上一篇文章的组合算法确定的设备相匹配

图片

最后,我对扩展集进行了测试,得出18432个组合:

具有这样的参数
MAX_STAGES = 30
MAX_DAYS_AT_STAGE = 50
SLIDING_FREQUENCY = 100
ROGUES_AT_BEGIN = 20
WINS_TO_REPRODUCE = 2
DEFEATS_TO_DIE = 2
POSSIBLE_BIRTH_QUANTITIES = [1, 2]

HTML_GSQUARE_SIDE = 4
HTML_GSQUARE_MARGIN = 1


图片

通常,您可以继续对此进行娱乐,但是趋势保持不变:在仿真过程中,基因型很快就开始在这些“能量中心”蓄积。显性基因型是最显着的“病灶”之一。

第6步-一切有意义吗?


如果我们现在转向问题的实际方面,我们需要了解这种遗传算法是否能够以比简单地组合所有基因型搜索更少的打架为代价找到正确的答案。答:是的,有能力削减证明:

遗传算法有效性的证明
1728 .

, .. 1728 .

, 1728 . «» — 2 ( ). , , 1728 / 2 = 864. .

:

MAX_STAGES = 8
MAX_DAYS_AT_STAGE = 20
SLIDING_FREQUENCY = 10
ROGUES_AT_BEGIN = 10
WINS_TO_REPRODUCE = 2
DEFEATS_TO_DIE = 2
POSSIBLE_BIRTH_QUANTITIES = [1]

HTML_GSQUARE_SIDE = 10
HTML_GSQUARE_MARGIN = 3

, 3-3-0-0-0-0-1:

image

:

image

, 3-3-0-0-0-0-1 547 . 87 (), 16% .

.

PS即使ROGUES_AT_BEGIN = 2且POSSIBLE_BIRTH_QUANTITIES = [1]也可以得到正确的答案这似乎令人惊讶,因为人口从未超过两个强盗。因为一个输了,另一个赢了,第一个死了,第二个生了一个后代。父母开始与这个后代竞争。后代或强或弱。因此,无情的选择轮会在父母和他的后代之间移动,直到他达到一个更好的点(他可以在分配的时间内达到这一点,所以这并不总是最好的)。

摘要


  1. 使用遗传算法可以解决该问题。
  2. «» .
  3. , , ( calculate_rate Rogue, ).
  4. 当然,在此程序上,您仍然可以进行实验和实验,以提高效率。例如,在某个时候,开始“禁止”明显丢失的基因型,不允许其主人打架甚至露面。因此,“领导者”的范围将缩小,他们必须准确地确定谁是最“强者”。

我将所有项目代码发布在github上

尊敬的社区,我将很高兴收到有关此主题的反馈。

All Articles