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

问题描述:

在一个3*3的方棋盘上放置着1,2,3,4,5,6,7,8八个数码,每个数码占一格,且有一个空格。这些数码可以在棋盘上移动,其移动规则是:与空格相邻的数码方格可以移入空格。现在的问题是:对于指定的初始棋局和目标棋局,给出数码的移动序列。该问题称八数码难题或者重排九宫问题。

1

算法流程图如下所示:

1

源代码为:

import copy

import numpy as np
import random
import time
import operator


# 遗传算法那解决八数码问题
# 八数码初始化函数,返回一个初始状态和一个目标状态,这里0代表八数码中的空格
def init():
    init_state = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8])
    target_state = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8])
    np.random.shuffle(init_state)
    np.random.shuffle(target_state)
    init_state = np.reshape(init_state, (3, 3))
    target_state = np.reshape(target_state, (3, 3))
    return init_state, target_state

# 计算单个状态适应度函数
def fitness_algorithm_single(init_state, target_state):
    total_distance = 0
    for i in range(1, 9):
        (init_row, init_column) = np.where(init_state == i)
        (target_row, target_column) = np.where(target_state == i)
        total_distance += abs(target_row - init_row) + abs(target_column - init_column)
    return int(total_distance)


# 移动并计算移动后的fitness
def move_and_fitness(num, current_state, target_state):
    temp_state = copy.deepcopy(current_state)
    zero_row, zero_column = np.where(temp_state == 0)
    zero_row, zero_column = int(zero_row), int(zero_column)
    if num == 0:  # 即方向为上,方向顺序为0-3,上下左右
        if zero_row - 1 >= 0:  # 判断是否还能向上移动,不能出边界,下面同理
            up_row, up_column = zero_row - 1, zero_column
            temp = temp_state[up_row][up_column]
            temp_state[zero_row][zero_column] = temp
            temp_state[up_row][up_column] = 0
            fitness = fitness_algorithm_single(temp_state, target_state)
            return fitness, temp_state
        else:
            fitness = 10000
            return fitness, temp_state
    if num == 1:
        if zero_row + 1 <= 2:
            down_row, down_column = zero_row + 1, zero_column
            temp = temp_state[down_row][down_column]
            temp_state[zero_row][zero_column] = temp
            temp_state[down_row][down_column] = 0
            fitness = fitness_algorithm_single(temp_state, target_state)
            return fitness, temp_state
        else:
            fitness = 10000
            return fitness, temp_state
    if num == 2:
        if zero_column - 1 >= 0:
            left_row, left_column = zero_row, zero_column - 1
            temp = temp_state[left_row][left_column]
            temp_state[zero_row][zero_column] = temp
            temp_state[left_row][left_column] = 0
            fitness = fitness_algorithm_single(temp_state, target_state)
            return fitness, temp_state
        else:
            fitness = 10000
            return fitness, temp_state
    if num == 3:
        if zero_column + 1 <= 2:
            right_row, right_column = zero_row, zero_column + 1
            temp = temp_state[right_row][right_column]
            temp_state[zero_row][zero_column] = temp
            temp_state[right_row][right_column] = 0
            fitness = fitness_algorithm_single(temp_state, target_state)
            return fitness, temp_state
        else:
            fitness = 10000
            return fitness, temp_state


# 计算一个基因序列的最大适应度
# 基因序列每个值都会产生一个新的状态同时也会产生新的适应度,在L长度的基因序列中,算出这个序列最好的适应度
def fitness_algorithm_for_population(init_state, target_state, population_list):
    for i in population_list:
        temp_state = copy.deepcopy(init_state)
        min_fitness = 10000
        for j in i:
            fitness, temp_state = move_and_fitness(j, temp_state, target_state)
            if fitness < min_fitness:
                min_fitness = fitness
        i.append(min_fitness)
    return population_list



# 随机生成解函数,即在遗传算法中随机生成一个基因
def init_solution(L):
    """
    :param L: 解的长度,即基因长度
    :return:单个解/基因的列表
    """
    # list为长度为L,范围为range(0,3)的列表,0-3代表上下左右的移动方向
    list = []
    for i in range(L):
        list.append(random.randrange(0, 4))
    return list


# 种群初始化函数
def init_population(M, L):
    """
    :param M: 种群大小
    :param L: 解的长度,即基因长度
    :return: M个种群组成的列表
    """
    population_list = []
    for i in range(M):
        population_list.append(init_solution(L))
    return population_list


# 根据适应度计算种群的个体被选作交叉的概率,使用y=-2x+100来调整,并排序
def select_algorithm(population_list):
    total = 0
    for i in population_list:
        fitness = i[-1]
        fitness = -1 * fitness + 100
        total += fitness
    for j in population_list:
        fitness = j[-1]
        fitness = -2 * fitness + 100
        select_prob = fitness / total
        j.append(select_prob)
    population_list = sorted(population_list, key=lambda key: key[-1], reverse=True)
    return population_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):
        temp = copy.deepcopy(list[i])
        retain_list.append(temp)
    eliminate_index = int(length * eliminate_prob)
    # 因为列表已经按照适应度排序,只需要计算出要淘汰的个数,直接将样本从列表尾部POP掉
    for j in range(eliminate_index):
        list.pop()
    # 将保留的retain_list的最后两位去掉,即曼哈顿距离和被选择的概率
    for k in retain_list:
        del k[-1]
        del k[-1]
    return retain_list, list


# 交叉算法,产生新的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, len(mother) - 2)  # 最后两位是适应度和选择概率
        for j in range(split_index):
            son.append(mother[j])
        for k in range(split_index, len(mother) - 2):
            son.append(father[k])
        new_list.append(son)
    return new_list


# 轮盘赌算法
def roulette_algorithm(list):
    total_probability = 0
    p = random.random()
    for i in list:
        total_probability = total_probability + i[-1]
        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

# 基因变异函数
def mutation_algorithm(list, mutation_prob):
    for i in list:
        for j in range(len(i) - 1):
            if random.random() < mutation_prob:
                direction = random.randrange(0, 4)
                i[j] = direction
    return list


# 遗传算法
def SA_algorithm(T=200, M=20, L=50, retain_prob=0.3, eliminate_prob=0.3, mutation_prob=0.3):
    """
    :param T: 最大繁衍次数
    :param M: 种群个体数量
    :param L: 基因长度
    :param retain_prob:优秀基因直接保留的比例
    :param eliminate_prob: 适应度差的基因淘汰的比例
    :param mutation_prob: 变异概率
    :return: bool
    """
    init_state, target_state = init()
    print("初始状态为:\n", init_state)
    print("目标状态为:\n", target_state)
    population_list = init_population(M, L)
    print("种群初始化为")
    for j in population_list:
        print(j)
    for i in range(T):
        print("当前是第{0}代".format(i))
        population_list = fitness_algorithm_for_population(init_state, target_state, population_list)  # 计算适应度
        for j in population_list:
            print(j)
        for i in population_list:
            if i[-1] == 0:  # 如果存在一个基因它的最小曼哈顿距离为0,则它就是目标解
                return True
        population_list = select_algorithm(population_list)  # 计算被选择概率,并按被选择概率降序排列
        print("当前种群状态为:")
        for j in population_list:
            print(j)
        retain_list, population_list = retain_and_eliminate(population_list, retain_prob,
                                                            eliminate_prob)  # 保留优良基因并淘汰适应度差的基因
        print("直接保留的样本为:")
        for j in retain_list:
            print(j)
        print("淘汰后剩下的样本为:")
        for j in population_list:
            print(j)
        population_list = cross_algorithm(population_list, M - len(retain_list))  # 交叉
        print("交叉后产生的样本为:")
        for j in population_list:
            print(j)
        population_list = mutation_algorithm(retain_list + population_list, mutation_prob)  # 变异
        print("变异后产生的样本为")
        for j in population_list:
            print(j)

    return False


def sa_algorithm_test(T, M, L, retain_prob, eliminate_prob, mutation_prob, num):
    success_case = 0
    fail_case = 0
    tic = time.time()
    for i in range(num):
        if SA_algorithm(T, M, L, 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=100, M=30, retain_prob=0.3, eliminate_prob=0.3, mutation_prob=0.03, num=100, L=200)

在实验参数为:T=300,M=100, retain_prob=0.3, eliminate_prob=0.3, mutation_prob=0.03,L=100条件下,结果为:

1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值