概要

我的上一篇写遗传算法解决排序问题,当中思想借鉴了遗传算法解决TSP问题,本质上可以认为这是一类问题,就是这样认为:寻找到一个序列X,使F(X)最大。

详解介绍

排序问题:寻找一个序列,使得这个序列的逆序对的倒数最大。

TSP问题:寻找一个序列,使得这个序列的总路径长的倒数最大。

这两个问题有一个共同的特点是,所有的节点都要用上,而使用遗传算法解决排序问题(每一个格子可以认为是一个节点),是需要从众多的节点之中寻找到某些节点构成一个序列X。

序列X必须满足的条件是:

  1. 相邻节点直接邻接
  2. 无重复节点(有重复的相当于走回头路)
  3. 序列的起点和终点必须是已知的点

第一个需要解决的问题是初代如何选择:

  1. 随机选择然后判断是否符合上面的三个条件(垃圾)
  2. 从起点开始随机生成到终点的序列

第二种做法的另一个问题就是随机性太大,可能会走比较长的路(其实也是可以采用的),为了解决这个问题,我才用了A*算法的启发式思维,将当前点和目标点的蔓哈顿距离作为适应度加入到优先队列中。

算法步骤

  1. 将起点加入到优先队列中
  2. 从优先队列中取出顶部顶点p0,将p0加入到Path(路径结果),如果p0是终点结束;
  3. 随机获取其周围的8个点中的一个p1
  4. 比较p0到目标点的曼哈顿距离|p0-target|  和p1到目标点的距离|p1-target|
  5. 如果|p1-target|<|p0-target|并且p1 not in Path, 将p1加入优先队列,p0<-p1;转到2

使用这种策略不仅引入了随机性,而且路径也比较合适,收敛比较快。

选择

这一步比较简单,就是普通的轮盘法就ok

交叉和变异

目前还没有想到策略(后面补充)

代码实现

 import random
import math
import copy
from tkinter import *
import tkinter.font as tkFont
import time, threading WIDTH = 100
HEIGHT = 100
MIN = 0
MAX = WIDTH * HEIGHT - 1 PATH_COUNT = 100
# 交叉概率
cross_p = 0.6
# 变异概率
variation_p = 0.4
# 变异次数
variation_times = 4 DIS_1 = 1.4
DIS_2 = 1 S = 0
D = 0 best_path = []
best_path_index = 0 res_fit = [] # 路径
paths = []
# 最优路径
# 迭代次数
ITERATION_COUNT = 100
#
direction_arr = [(-1, -1), (0, -1), (1, -1), (-1, 0), (1, 0), (-1, 1), (0, 1), (1, 1)] def is_valid(point):
if point[0] < 0 or point[1] < 0 or point[0] >= WIDTH or point[1] >= HEIGHT:
return False
return True # 计算欧式距离
def distance(p1, p2):
return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) # 标号转坐标
def mark2position(mark):
return (mark % WIDTH, int(mark / WIDTH)) def position2mark(position):
return position[1] * WIDTH + position[0] # 5 6 7
# 3 4
# 0 1 2
def generate_one_path(start, end):
res = []
res.append(start) s = start
target_point = mark2position(end)
dis = distance(mark2position(start), target_point) while (s != end):
pos = mark2position(s)
r = random.randint(0, 7)
pos = (pos[0] + direction_arr[r][0], pos[1] + direction_arr[r][1])
temp_dis = distance(pos, target_point)
if is_valid(pos) and temp_dis <= dis:
s = position2mark(pos)
dis = temp_dis
res.append(s)
return res # 初代
def init(count):
res = []
for i in range(0, count):
res.append(generate_one_path(S, D))
return res # 计算一条路径的适应度值
def one_path_fit_val(path):
sm = 0
for i in range(1, len(path)):
w = int(math.fabs(path[i - 1] - path[i]))
if w == 1 or w == WIDTH:
sm += DIS_2
else:
sm += DIS_1
return MAX / sm # 计算适应度值
def fitness():
res = []
max_fit = -1
global best_path
global best_path_index temp_best_path = [] for i in range(len(paths)):
f = one_path_fit_val(paths[i])
res.append(f)
if f > max_fit:
max_fit = f
temp_best_path = paths[i]
best_path_index = i
best_path = copy.deepcopy(temp_best_path)
res_fit.append(max_fit)
return res # 累计概率
def cumulative_probability(fits):
res = []
sm = sum(fits)
temp = fits[0] / sm
res.append(temp)
for i in range(1, len(fits)):
res.append(res[i - 1] + fits[i] / sm)
return res # 选择 产生下一代
def choose(pArr, count):
res = []
for i in range(count):
p = random.random()
for j in range(len(pArr)):
if p <= pArr[j]:
res.append(paths[j])
break
return res def cross_one_times(path1, path2):
# 求交集
temp = list(set(path1[1:-1]).intersection(set(path2[1:-1])))
sz = len(temp)
if sz == 0:
return (path1, path2)
r = random.random()
if r > cross_p:
index = random.randint(0, sz - 1)
e = temp[index]
t1 = path1.index(e)
t2 = path2.index(e)
p1 = path1[:t1]
p2 = path2[t2:]
p3 = path2[:t2]
p4 = path1[t1:]
p1.extend(p2)
p3.extend(p4)
return (p1, p3)
else:
return (path1, path2) def cross():
n = len(paths)
res = []
for i in range(1, n, 2):
p = cross_one_times(paths[i], paths[i - 1])
res.extend(p) # 奇数情况
if len(res) < n:
res.append(paths[n - 1])
return res # 判断三点之间是否联通
def is_valid_3_mark(m1, m2, m3):
# 重复
if m1 == m2 or m1 == m3 or m2 == m3:
return False
if m2 < MIN or m2 > MAX:
return False
# 不联通
if not (m1 + 1 == m2 or m1 - 1 == m2 or m1 + WIDTH == m2 or m1 - WIDTH == m2
or m1 + WIDTH + 1 == m2 or m1 + WIDTH - 1 == m2
or m1 - WIDTH + 1 == m2 or m1 - WIDTH - 1 == m2):
return False
# 不联通
if not (m3 + 1 == m2 or m3 - 1 == m2 or m3 + WIDTH == m2 or m3 - WIDTH == m2
or m3 + WIDTH + 1 == m2 or m3 + WIDTH - 1 == m2
or m3 - WIDTH + 1 == m2 or m3 - WIDTH - 1 == m2):
return False
return True def variation_one_times(path):
r = random.random()
if r < variation_p:
return path
else:
sz = len(path)
if sz <= 2:
return path
# 变异点
prob_mark = []
var_index = random.randint(1, sz - 2)
pre_mark = path[var_index - 1]
cnt_mark = path[var_index]
next_mark = path[var_index + 1]
# 8中情况
temp_mark = [cnt_mark + 1, cnt_mark - 1, cnt_mark + WIDTH, cnt_mark - WIDTH, cnt_mark + WIDTH + 1,
cnt_mark + WIDTH - 1, cnt_mark - WIDTH - 1, cnt_mark - WIDTH + 1]
for e in temp_mark:
if is_valid_3_mark(pre_mark, e, next_mark):
prob_mark.append(e) if len(prob_mark) == 0:
return path
changed_mark = prob_mark[random.randint(0, len(prob_mark) - 1)]
path[var_index] = changed_mark
return path def variation():
res = paths
for i in range(variation_times):
temp = []
for e in res:
temp.append(variation_one_times(e))
res = temp
return res def output(g, f):
print("第" + str(g) + "代:最优路径:", end="", file=f)
print(best_path, end="", file=f)
print("适应度: ", end="", file=f)
print(fits[best_path_index], file=f)
for i, path in enumerate(paths):
print(str(i + 1) + ". ", end="", file=f)
print(path, end="", file=f)
print("适应度值:" + str(fits[i]), file=f) def mark_screen_position(mark, x_min, y_max):
temp_p = mark2position(mark)
x = temp_p[0] - x_min
y = y_max - temp_p[1]
return (x, y) def show(path, title):
canvas_width = 1000
point_r = 2
show_mark_min_width = 10
temp = []
for p in path:
temp.append(p % 100)
x_min = min(temp)
x_max = max(temp)
temp.clear()
for p in path:
temp.append(int(p / 100))
y_min = min(temp)
y_max = max(temp)
d = max(x_max - x_min + 1, y_max - y_min + 1)
grid_width = int(canvas_width / d)
canvas_width = grid_width * d
win = Tk()
win.title(title)
win.geometry(str(canvas_width) + "x" + str(canvas_width) + "+100+100")
can = Canvas(win, width=canvas_width, height=canvas_width, bg="white")
for i in range(0, canvas_width, grid_width):
can.create_line((0, i), (canvas_width, i)) for i in range(0, canvas_width, grid_width):
can.create_line((i, 0), (i, canvas_width))
ft = tkFont.Font(root=win, family='Fixdsys', size=int(20 / 4), weight=tkFont.BOLD)
if grid_width >= show_mark_min_width:
for x in range(0, d):
for y in range(0, d):
s = position2mark((x + x_min, y_max - y))
can.create_text(x * grid_width + grid_width / 2, y * grid_width + grid_width / 2, text=s,
font=ft)
sz = len(path)
for i in range(0, sz - 1):
p1 = mark_screen_position(path[i], x_min, y_max)
p2 = mark_screen_position(path[i + 1], x_min, y_max)
can.create_line((p1[0] * grid_width + grid_width / 2, p1[1] * grid_width + grid_width / 2),
(p2[0] * grid_width + grid_width / 2, p2[1] * grid_width + grid_width / 2), fill="red", width=3)
if i == 0: {
can.create_oval(
(p1[0] * grid_width + grid_width / 2 - point_r, p1[1] * grid_width + grid_width / 2 - point_r,
p1[0] * grid_width + grid_width / 2 + point_r, p1[1] * grid_width + grid_width / 2 + point_r),
fill="blue")
}
can.create_oval((p2[0] * grid_width + grid_width / 2 - point_r, p2[1] * grid_width + grid_width / 2 - point_r,
p2[0] * grid_width + grid_width / 2 + point_r, p2[1] * grid_width + grid_width / 2 + point_r),
fill="blue")
can.pack()
win.mainloop() # run point
random.seed()
S = random.randint(MIN, MAX)
D = random.randint(MIN, MAX)
while (S == D):
D = random.randint(MIN, MAX)
g = 1
fp = open("1.txt", "w", encoding="utf-8") # 初代
paths = init(PATH_COUNT)
fits = fitness() # 适应度计算
output(g, fp)
g = g + 1 origin_best_path = [] for i in range(ITERATION_COUNT):
pArr = cumulative_probability(fits) # 累计概率
paths = choose(pArr, PATH_COUNT - 1) # 选择
paths = cross() # 交叉
paths = variation() # 变异
paths.append(best_path)
if i == 0:
origin_best_path = copy.deepcopy(best_path)
fits = fitness() # 适应度计算
output(g, fp)
g = g + 1
fp.flush()
fp.close() fp = open("2.txt", "w", encoding="utf-8")
fp.write("最大适应度值列表:\n")
for e in res_fit:
fp.write(format(e, ".2f"))
fp.write(" ")
fp.flush()
fp.close() t1 = threading.Thread(target=show, args=(origin_best_path, "初代最好的路径"))
t2 = threading.Thread(target=show, args=(best_path, "最好的路径"))
t1.start()
t2.start()
t1.join()
t2.join()

效果图

图形显示

遗传算法解决寻路问题——Python描述的更多相关文章

  1. python描述符(descriptor)、属性(property)、函数(类)装饰器(decorator )原理实例详解

     1.前言 Python的描述符是接触到Python核心编程中一个比较难以理解的内容,自己在学习的过程中也遇到过很多的疑惑,通过google和阅读源码,现将自己的理解和心得记录下来,也为正在为了该问题 ...

  2. Python描述符(descriptor)解密(转)

    原文:http://www.geekfan.net/7862/ Python中包含了许多内建的语言特性,它们使得代码简洁且易于理解.这些特性包括列表/集合/字典推导式,属性(property).以及装 ...

  3. 遗传算法解决旅行商问题(TSP)

    这次的文章是以一份报告的形式贴上来,代码只是简单实现,难免有漏洞,比如循环输入的控制条件,说是要求输入1,只要输入非0就行.希望会帮到以后的同学(*^-^*) 一.问题描述 旅行商问题(Traveli ...

  4. Python描述符的使用

    Python描述符的使用 前言 作为一位python的使用者,你可能使用python有一段时间了,但是对于python中的描述符却未必使用过,接下来是对描述符使用的介绍 场景介绍 为了引入描述符的使用 ...

  5. Python——描述符(descriptor)解密

    本文由 极客范 - 慕容老匹夫 翻译自 Chris Beaumont.欢迎加入极客翻译小组,同我们一道翻译与分享.转载请参见文章末尾处的要求. Python中包含了许多内建的语言特性,它们使得代码简洁 ...

  6. 杂项之python描述符协议

    杂项之python描述符协议 本节内容 由来 描述符协议概念 类的静态方法及类方法实现原理 类作为装饰器使用 1. 由来 闲来无事去看了看django中的内置分页方法,发现里面用到了类作为装饰器来使用 ...

  7. 【转载】Python 描述符简介

    来源:Alex Starostin 链接:www.ibm.com/developerworks/cn/opensource/os-pythondescriptors/ 关于Python@修饰符的文章可 ...

  8. paip. 解决php 以及 python 连接access无效的参数量。参数不足,期待是 1”的错误

    paip. 解决php 以及 python 连接access无效的参数量.参数不足,期待是 1"的错误 作者Attilax  艾龙,  EMAIL:1466519819@qq.com  来源 ...

  9. 解决删除/升级Python导致Ubuuntu无法进入桌面的问题

    找到问题的原因后于是换个思路,想大概修复了python,Ubuntu进入桌面应该也就没啥问题了.于是重新安装Python发现还是无济于事.也通过/usr/bin/python: can't find ...

随机推荐

  1. Spark分区实例(teacher)

    package URL1 import org.apache.spark.Partitioner import scala.collection.mutable class MyPartitioner ...

  2. php使用装饰模式无侵入式加缓存

    <?php namespace App\Services; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\ ...

  3. js多元运算

    for(var i=1;i<=100;i++){ var f = i%3 == 0, b = i%5 == 0; if(f){ if(b){ console.log("FizzBuzz ...

  4. DOM事件练习 II

    select框联动效果 需求:当在textarea中输入内容,点击留言按钮,会添加到浏览器中,最新留言出现在最顶端. <!DOCTYPE html> <html lang=" ...

  5. 面试题:检测一个ip的真实性,如果真实,确定其是不是在某一范围内

    例题: 现有一个ip 10.2.1.71 ,检测该ip是否为真实有效的ip,并判断该ip是否在10.2.1.1——10.2.1.255之间 解题思路:用正则表达式检测ip的真实性,如果真实,将该ip转 ...

  6. OKR工作法 目标明确的写下来 - 结果记录- 校准

    1.o - objective - 旅程的目的地 - 方向 - 定性的 2.kr - key result - 旅途的下一跳和关键节点 - 定量的 - 需要停下来校准 ################ ...

  7. CF171C 【A Piece of Cake】

    遵从题意枚举暴力读人n,再求出$\sum^n_1a[i]*i$然后输出答案,(记得开个long long以免炸掉)以下是代码: #include<bits/stdc++.h> using ...

  8. Known Notation括号匹配类问题(2014年ACM/ICPC 亚洲区域赛牡丹江)

    题意: 给你数字或 * 的串,你可以交换一个*和数字.在最前面添1.在一个地方插入*,问你使串满足入栈出栈的(RNP)运算法则. 思路: 引用:https://blog.csdn.net/u01158 ...

  9. monggoDB添加到windows服务

    ----------------mongoDB安装------------------------------- 1.下载mongoDB安装包安装完毕后,配置环境变量 D:\Program Files ...

  10. 04 Redis主从同步

    redis主从同步 原理:1. 从服务器向主服务器发送 SYNC 命令.2. 接到 SYNC 命令的主服务器会调用BGSAVE 命令,创建一个 RDB 文件,并使用缓冲区记录接下来执行的所有写命令.3 ...