人工智能-遗传算法解决八皇后问题-python源码

问题描述:

八皇后问题,一个古老而著名的问题,是回溯算法的典型案例。该问题由国际西洋棋棋手马克斯·贝瑟尔于 1848 年提出:在 8×8 格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

人工智能-遗传算法解决八皇后问题-python源码_第1张图片

算法解决流程图为:人工智能-遗传算法解决八皇后问题-python源码_第2张图片

源代码为:

import numpy as np
import random
import time
import operator


# 遗传算法解决八皇后问题
# 八皇后初始化函数
def init():
    cache = {}
    m = np.zeros((8, 8), dtype=int)
    for i in range(0, 8):
        temp = random.randrange(0, 8)
        m[temp, i] = 1
        cache["queen" + str(i)] = [temp, i]
    return m, cache


# 计算一个八皇后状态的适应度,计算方法为皇后无碰撞数量
def fitness_algorithm_single(coord_cache):
    weight = 0
    for i in range(0, 8):
        row, column = coord_cache["queen" + str(i)]
        for j in range(i + 1, 8):
            _row, _column = coord_cache["queen" + str(j)]
            if _row - row == j - i or _row - row == i - j:
                weight += 1
            if _row == row:
                weight += 1
    return 28 - weight  # 28表示最优解状态互不攻击的皇后数目,减去碰撞数目即为适应度


# 为一个种群计算适应度
def fitness_algorithm_for_list(list):
    for i in list:
        fitness = fitness_algorithm_single(i)
        i["fitness"] = [fitness]
    return list


# 根据适应度计算出每个样本被选择的概率
def select_probability(list):
    total_fitness = 0
    specimen_list = list
    for i in specimen_list:
        fitness = i["fitness"]
        total_fitness += fitness[0]
    for j in specimen_list:
        fitness = j["fitness"]
        j["select"] = [fitness[0] / total_fitness]
    return specimen_list


# 初始化种群函数,并依据适应度进行了排序
def init_population(M):
    specimen_list = []  # 种群列表
    for i in range(M):
        m, coord_cache = init()
        specimen_list.append(coord_cache)
    specimen_list = fitness_algorithm_for_list(specimen_list)
    # 对适应度进行排序   specimen_list的结构为   [{"queen0":[row,column]  ........  "weight":[weight]}]
    specimen_list = sorted(specimen_list, key=lambda keys: keys["fitness"], reverse=True)  # 降序
    specimen_list = select_probability(specimen_list)  # 计算被选为父母的概率
    return specimen_list


# 轮盘赌算法
def roulette_algorithm(list):
    total_probability = 0
    p = random.random()
    for i in list:
        total_probability = total_probability + i["select"][0]
        if total_probability > p:
            break
    return i


# 运行两次轮盘赌得到父母基因,这两个样本应该不同
def sa_roulette(list):
    # fix_bug
    # 这段代码为了修复种群过小时,经过多代繁衍,所有种群收敛到趋近于一个值,比如10个种群全部适应度为27
    # 导致轮盘赌算法陷入无限循环
    count = 0
    temp = list[0]
    for i in list:
        if operator.eq(temp, i):
            count += 1
    if count == len(list):
        mother = list[0]
        father = list[1]
        return mother, father
    # *******************************************************************************************
    mother = roulette_algorithm(list)
    while True:
        father = roulette_algorithm(list)
        if operator.ne(mother, father):
            break
    return mother, father


# 繁衍算法,产生新的M个种群
def cross_algorithm(origin_list, M):
    new_list = []
    for i in range(M):
        mother, father = sa_roulette(origin_list)
        son = {}
        split_index = random.randrange(0, 8)
        for j in range(split_index):
            son["queen" + str(j)] = father["queen" + str(j)]
        for k in range(split_index, 8):
            son["queen" + str(k)] = mother["queen" + str(k)]
        new_list.append(son)
    return new_list


# 优秀的基因直接保留,适应度较低的样本按一定的概率淘汰
def retain_and_eliminate(list, retain_prob, eliminate_prob):
    retain_list = []
    length = len(list)
    # 保留retain_prob百分比的样本
    retain_index = int(length * retain_prob)
    for i in range(retain_index):
        retain_list.append(list[i])
    eliminate_index = int(length * eliminate_prob)
    # 因为列表已经按照适应度排序,只需要计算出要淘汰的个数,直接将样本从列表尾部POP掉
    for j in range(eliminate_index):
        list.pop()
    return retain_list, list


# 基因变异函数,每个样本的皇后位置会根据mutation_prob的概率随机调整
def mutation_algorithm(list, mutation_prob):
    for i in range(len(list)):
        if random.random() < mutation_prob:
            row = random.randrange(0, 8)
            column = random.randrange(0, 8)
            list[i]["queen" + str(column)] = [row, column]

    return list


# 判断种群中是否有满足条件的个体,即无碰撞数量为28
def screening_population(population_list):
    for i in population_list:
        if fitness_algorithm_single(i) == 28:
            return True
    return False


def SA_algorithm(T=50, M=10, retain_prob=0.3, eliminate_prob=0.3, mutation_prob=0.1):
    """
    :param T: 最大繁殖代数
    :param M: 种群大小
    :param retain_prob: 每一代中优良基因直接保留的百分比
    :param eliminate_prob: 每一代中淘汰的百分比
    :param mutation_prob: 变异百分比
    :return:bool
    """
    population_list = init_population(M)  # 初始化种群,已经按适应度降序排列
    print("初始种群的状态为:", )
    for j in population_list:
        print(j)
    for i in range(T):
        if screening_population(population_list):
            return True
        print("当前种群的状态为:",)
        for j in population_list:
            print(j)
        retain_list, left_list = retain_and_eliminate(population_list, retain_prob, eliminate_prob)  # 保留优良基因和淘汰适应度低的基因
        print("直接保留的个体为:")
        for j in retain_list:
            print(j)
        print("淘汰之后剩余的个体")
        for j in left_list:
            print(j)
        cross_list = cross_algorithm(left_list, M - len(retain_list))  # 基因交叉,因为保留了一部分优良基因,这里产生的基因等于M-保留的基因数
        print("交叉产生的新个体为:")
        for j in cross_list:
            print(j)
        population_list = mutation_algorithm(retain_list + cross_list, mutation_prob)  # 基因变异
        population_list = fitness_algorithm_for_list(population_list)
        population_list = select_probability(population_list)
        population_list = sorted(population_list, key=lambda keys: keys["fitness"], reverse=True)  # 降序
        print("变异后产生的种群为:")
        for j in population_list:
            print(j)
    return False


def sa_algorithm_test(T, M, retain_prob, eliminate_prob, mutation_prob, num=1000):
    success_case = 0
    fail_case = 0
    tic = time.time()
    for i in range(num):
        if SA_algorithm(T, M, retain_prob, eliminate_prob, mutation_prob):
            success_case += 1
            print("第{0}个例子发现最优解".format(i))
        else:
            fail_case += 1
            print("第{0}个例子失败".format(i))
    toc = time.time()
    print("{0}个例子中成功解决的例子为:{1}".format(num, success_case))
    print("{0}个例子成功解决的百分比为:{1}".format(num, success_case / num))
    print("{0}个例子中失败的例子为:{1}".format(num, fail_case))
    print("{0}个例子失败的百分比为:{1}".format(num, fail_case / num))
    print("{0}个例子运行算法所需的时间为:{1}秒".format(num, toc - tic))


sa_algorithm_test(T=300, M=50, retain_prob=0.3, eliminate_prob=0.3, mutation_prob=0.3, num=1000)

在实验参数为:T=100,M=30, retain_prob=0.3, eliminate_prob=0.3, mutation_prob=0.3情况下,实验结果为:

人工智能-遗传算法解决八皇后问题-python源码_第3张图片

人工智能-遗传算法解决八皇后问题-python源码_第4张图片

你可能感兴趣的:(人工智能,python,遗传算法)