利用遗传算法解决tsp问题与多峰函数问题

题目

1.tsp问题

以下是中国34个城市的坐标值,利用遗传算法求出最短路径和最短路径距离。
北京 ;116.46;39.92
天津 ;117.2;39.13
上海 ;121.48;31.22
重庆 ;106.54;29.59
拉萨 ;91.11;29.97
乌鲁木齐 ;87.68;43.77
银川 ;106.27;38.47
呼和浩特 ;111.65;40.82
南宁 ;108.33;22.84
哈尔滨 ;126.63;45.75
长春 ;125.35;43.88
沈阳 ;123.38;41.8
石家庄 ;114.48;38.03
太原 ;112.53;37.87
西宁 ;101.74;36.56
济南 ;117;36.65
郑州;113.6;34.76
南京;118.78;32.04
合肥;117.27;31.86
杭州;120.19;30.26
福州;119.3;26.08
南昌;115.89;28.68
长沙;113;28.21
武汉;114.31;30.52
广州;113.23;23.16
台北;121.5;25.05
海口;110.35;20.02
兰州;103.73;36.03
西安;108.95;34.27
成都;104.06;30.67
贵阳;106.71;26.57
昆明;102.73;25.04
香港;114.1;22.2
澳门;113.33;22.13

2.多峰函数优化问题

Michalewicz函数如下:
在这里插入图片描述

1.TSP问题

import random
import math
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from math import radians, cos, sin, asin, sqrt

class GA(object):
    def __init__(self, num_city, num_total, iteration, data):
        self.num_city = num_city
        self.num_total = num_total
        self.scores = []
        self.iteration = iteration
        self.location = data

        self.ga_choose_ratio = 0.2
        self.mutate_ratio = 0.05
        # fruits中存每一个个体是下标的list
        self.dis_mat = self.city_distance(data)
        self.fruits = self.greedy_init(self.dis_mat,num_total,num_city)

        # 显示初始化后的最佳路径
        scores = self.compute_adp(self.fruits)
        sort_index = np.argsort(-scores)
        init_best = self.fruits[sort_index[0]]
        init_best = self.location[init_best]
        print(self.dis_mat)
        # 存储每个iteration的结果,画出收敛图
        self.iter_x = [0]
        self.iter_y = [1. / scores[sort_index[0]]]

    def random_init(self, num_total, num_city):     #随机初始化路径
        tmp = [x for x in range(num_city)]
        result = []
        for i in range(num_total):
            random.shuffle(tmp)
            result.append(tmp.copy())
        return result

    def greedy_init(self, dis_mat, num_total, num_city):    #贪心初始化
        start_index = 0
        result = []
        for i in range(num_total):
            rest = [x for x in range(0, num_city)]
            # 所有起始点都已经生成了
            if start_index >= num_city:
                start_index = np.random.randint(0, num_city)
                result.append(result[start_index].copy())
                continue
            current = start_index
            rest.remove(current)
            # 找到一条最近邻路径
            result_one = [current]
            while len(rest) != 0:
                tmp_min = math.inf
                tmp_choose = -1
                for x in rest:
                    if dis_mat[current][x] < tmp_min:
                        tmp_min = dis_mat[current][x]
                        tmp_choose = x

                current = tmp_choose
                result_one.append(tmp_choose)
                rest.remove(tmp_choose)
            result.append(result_one)
            start_index += 1
        return result


    def earth_distance(self,lon1, lat1, lon2, lat2):
        """
        Calculate the great circle distance between two points
        on the earth (specified in decimal degrees)
        """
        # 将十进制度数转化为弧度
        lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])

        # haversine公式
        dlon = lon2 - lon1
        dlat = lat2 - lat1
        a = sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2
        c = 2 * asin(sqrt(a))
        r = 6371  # 地球平均半径,单位为公里
        return c * r

    def calc_path_distance(self,distance_matrix, solution):
        """
        Total distance of the current solution path.
        """
        N = distance_matrix.shape[0]
        cur_dist = 0
        for i in range(N):
            cur_dist += distance_matrix[solution[i % N], solution[(i + 1) % N]]
        return cur_dist

    def city_distance(self,cities):
        N = len(cities)
        distance_matrix = np.zeros((N, N))
        for i in range(N):
            loni, lati = cities[i]
            for j in range(i, N):
                lonj, latj = cities[j]
                dist = self.earth_distance(loni, lati, lonj, latj)
                distance_matrix[i][j] = distance_matrix[j][i] = dist

        distance_matrix = np.round(distance_matrix, 2)
        print(distance_matrix)
        return distance_matrix

    # 计算不同城市之间的距离
    def compute_dis_mat(self, num_city, location):
        dis_mat = np.zeros((num_city, num_city))
        for i in range(num_city):
            for j in range(num_city):
                if i == j:
                    dis_mat[i][j] = np.inf
                    continue
                a = location[i]
                b = location[j]
                tmp = np.sqrt(sum([(x[0] - x[1]) ** 2 for x in zip(a, b)]))
                dis_mat[i][j] = tmp
        return dis_mat

    # 计算路径长度
    def compute_pathlen(self, path, dis_mat):
        try:
            a = path[0]
            b = path[-1]
        except:
            import pdb
            pdb.set_trace()
        result = dis_mat[a][b]
        for i in range(len(path) - 1):
            a = path[i]
            b = path[i + 1]
            result += dis_mat[a][b]
        return result

    # 计算种群适应度
    def compute_adp(self, fruits):
        adp = []
        for fruit in fruits:
            if isinstance(fruit, int):
                import pdb
                pdb.set_trace()
            length = self.compute_pathlen(fruit, self.dis_mat)
            adp.append(1.0 / length)        #路径越长,种群适应度越差
        return np.array(adp)

    def swap_part(self, list1, list2):
        index = len(list1)
        list = list1 + list2
        list = list[::-1]
        return list[:index], list[index:]

    def ga_cross(self, x, y):       # 交叉
        len_ = len(x)
        assert len(x) == len(y)
        path_list = [t for t in range(len_)]
        order = list(random.sample(path_list, 2))   #从里面随机选择n个数
        order.sort()
        start, end = order

        # 找到冲突点并存下他们的下标,x中存储的是y中的下标,y中存储x与它冲突的下标,就是找不一样的点
        tmp = x[start:end]
        x_conflict_index = []
        for sub in tmp:
            index = y.index(sub)
            if not (index >= start and index < end):
                x_conflict_index.append(index)

        y_confict_index = []
        tmp = y[start:end]
        for sub in tmp:
            index = x.index(sub)
            if not (index >= start and index < end):
                y_confict_index.append(index)

        assert len(x_conflict_index) == len(y_confict_index)

        # 交叉
        tmp = x[start:end].copy()
        x[start:end] = y[start:end]
        y[start:end] = tmp

        # 解决冲突
        for index in range(len(x_conflict_index)):
            i = x_conflict_index[index]
            j = y_confict_index[index]
            y[i], x[j] = x[j], y[i]

        assert len(set(x)) == len_ and len(set(y)) == len_
        return list(x), list(y)

    # 遗传父母
    def ga_parent(self, scores, ga_choose_ratio):
        sort_index = np.argsort(-scores).copy()     # 排序然后提取他排序前的位置
        sort_index = sort_index[0:int(ga_choose_ratio * len(sort_index))]   # 按照ratio选择
        parents = []
        parents_score = []
        for index in sort_index:
            parents.append(self.fruits[index])
            parents_score.append(scores[index])
        return parents, parents_score

    # 选择算子,轮盘赌
    def ga_choose(self, genes_score, genes_choose):
        sum_score = sum(genes_score)
        score_ratio = [sub * 1.0 / sum_score for sub in genes_score]
        rand1 = np.random.rand()
        rand2 = np.random.rand()
        for i, sub in enumerate(score_ratio):
            if rand1 >= 0:
                rand1 -= sub
                if rand1 < 0:
                    index1 = i
            if rand2 >= 0:
                rand2 -= sub
                if rand2 < 0:
                    index2 = i
            if rand1 < 0 and rand2 < 0:
                break
        return list(genes_choose[index1]), list(genes_choose[index2])

    #选择算子,锦标赛
    def ga_choose1(self,genes_score,genes_choose):
        index1=0
        index2=0
        index=0
        now=0
        for i in genes_score:
            if i>now:
                i=now
                index1=index
                index2=index1
            index+=1
        return list(genes_choose[index1]),list(genes_choose[index2])
    # 变异
    def ga_mutate(self, gene):
        path_list = [t for t in range(len(gene))]
        order = list(random.sample(path_list, 2))
        start, end = min(order), max(order)
        tmp = gene[start:end]
        # np.random.shuffle(tmp)
        tmp = tmp[::-1]
        gene[start:end] = tmp
        return list(gene)

    # 遗传
    def ga(self):
        # 获得优质父代
        scores = self.compute_adp(self.fruits)  # 计算适应度
        # 选择部分优秀个体作为父代候选集合
        parents, parents_score = self.ga_parent(scores, self.ga_choose_ratio)
        tmp_best_one = parents[0]
        tmp_best_score = parents_score[0]
        # 新的种群fruits
        fruits = parents.copy()
        # 生成新的种群
        while len(fruits) < self.num_total:
            # 轮盘赌方式就用ga_choose,锦标赛方式就用ga_choose1
            gene_x, gene_y = self.ga_choose(parents_score, parents)

            # 交叉
            gene_x_new, gene_y_new = self.ga_cross(gene_x, gene_y)
            # 变异
            if np.random.rand() < self.mutate_ratio:
                gene_x_new = self.ga_mutate(gene_x_new)
            if np.random.rand() < self.mutate_ratio:
                gene_y_new = self.ga_mutate(gene_y_new)
            x_adp = 1. / self.compute_pathlen(gene_x_new, self.dis_mat)
            y_adp = 1. / self.compute_pathlen(gene_y_new, self.dis_mat)
            # 将适应度高的放入种群中
            if x_adp > y_adp and (not gene_x_new in fruits):
                fruits.append(gene_x_new)
            elif x_adp <= y_adp and (not gene_y_new in fruits):
                fruits.append(gene_y_new)

        self.fruits = fruits

        return tmp_best_one, tmp_best_score

    #开跑
    def run(self):
        BEST_LIST = None
        best_score = -math.inf
        self.best_record = []
        for i in range(1, self.iteration + 1):
            tmp_best_one, tmp_best_score = self.ga()
            self.iter_x.append(i)
            self.iter_y.append(1. / tmp_best_score)
            if tmp_best_score > best_score:
                best_score = tmp_best_score
                BEST_LIST = tmp_best_one
            self.best_record.append(1./best_score)
            print(i,1./best_score)
        print(1./best_score)
        return self.location[BEST_LIST], 1. / best_score


# 读取数据
def read_tsp(path):
    data=pd.read_csv(path,sep=';',header=None)
    print(data)
    data=data.loc[:,:]
    return data


data = read_tsp('data/test.csv')

data = np.array(data)
data = data[:, 1:]
print(data)
Best, Best_path = math.inf, None

model = GA(num_city=data.shape[0], num_total=25, iteration=500, data=data.copy())
path, path_len = model.run()

if path_len < Best:
    Best = path_len
    Best_path = path
print(Best_path)
# 加上一行因为会回到起点
fig, axs = plt.subplots(2, 1, sharex=False, sharey=False)
axs[0].scatter(Best_path[:, 0], Best_path[:,1])
Best_path = np.vstack([Best_path, Best_path[0]])
axs[0].plot(Best_path[:, 0], Best_path[:, 1])
axs[0].set_title('Planning results')
iterations = range(model.iteration)
best_record = model.best_record
axs[1].plot(iterations, best_record)
axs[1].set_title('Convergence curve')
plt.show()

多峰函数优化

import random
import math
import matplotlib.pyplot as plt
personNum=1000 #种群数量
mutationProbability=0.9 #变异概率
iteration=50 #假设迭代50次即终止
length=30

def getAbsList(theList):
    for i in range(len(theList)):
        if theList[i]<0:
            theList[i]=theList[i]*(-1)
    return theList

# 功能:生成初始化种群
# 参数:personNum为种群数量,length为种群每个个体编码的位数
def initialPopulation(personNum=50,length=30):
    totalPopulation=[]
    while len(totalPopulation)!=personNum:
        person=[]
        for i in range(length):
            temp = random.uniform(-1, 1)  # 生成-1<=X<=1的数字
            if temp<0:
                person.append(0)
            else:
                person.append(1)
        theStr = ''
        for item in person:
            theStr += str(item)
        #print(theStr)
        if theStr not in totalPopulation:
            if evaluate(theStr)>0:
                totalPopulation.append(theStr)
        #print(len(totalPopulation))
    return totalPopulation


# 函数功能:将编码转换为x,y的十进制解
def decode(onePerson,length=30):
    index=0
    data=[]
    while index<length:
        #print(onePerson[index:index+15])
        data.append(int(onePerson[index:index+15],2)*math.pi/32678)
        index+=15
    #print("没报错")
    return data


# 功能:计算x,y对应的函数值
# 参数:一个个体的编码
def evaluate(onePerson,length=30):
    data=decode(onePerson,length)
    result=0
    for i in range(int(length/15)):
        x=data[i]
        result+=math.sin(x) * math.pow(math.sin((i+1)*x * x / math.pi), 20)
    #print(result)
    return result

# 功能:获取一个父母进行交叉
# 输出:返回的是一个双亲在population的index
def getParents(evalList):
    temp = random.uniform(0, 1)
    #print(temp)
    portionList=[];theSum=0
    totalEval=sum(evalList)
    #print(totalEval)
    for eval in evalList:
        theSum+=eval/totalEval
        portionList.append(theSum)
    location=0
    while(temp>portionList[location]):
        location+=1
    #print('location=',location)
    return location


# 输入:两个person
# 输出:生成的子代person编码
def getCross(father,mother,length=30):
    theVisit=[]
    crossLocation=random.randint(0,length-1)
    theVisit.append(crossLocation)
    #print(crossLocation)
    child=''
    child += father[0:crossLocation]
    child += mother[crossLocation:length]
    while evaluate(child)<0:
        #print("重新交叉")
        while crossLocation in theVisit and len(theVisit)<length:
            crossLocation = random.randint(0, length-1)
            #print(crossLocation)
            child += father[0:crossLocation]
            child += mother[crossLocation:]
        theVisit.append(crossLocation)
        if len(theVisit)>=length:
            child=father
        #print(len(child))
    return child


# 功能:进行变异
def getVari(person,length=30):
    #print(person)
    temp = random.uniform(0, 1)
    if temp<mutationProbability:
        #print('变异')
        location=random.randint(0,length-1)
        #print(location)
        tempStr=person[0:location]
        tempStr+=str(1-int(person[location]))
        tempStr+=person[location+1:]
        if evaluate(tempStr)>evaluate(person):
            return tempStr
    return person


if __name__=='__main__':
    theScore=[]
    bestPerson=[]
    theBestEval=0
    print("输入d的大小:")
    d=int(input())
    length=d*15
    population = initialPopulation(personNum,length)
    flag = 0
    bestRecord=[]
    while flag!=iteration:
        print("第",flag+1,"代")
        evalList=[]
        tempPopulation=[]
        for person in population:
            evalList.append(evaluate(person,length))
        maxEval=max(evalList)
        print('maxEval=',maxEval)
        theIndex=evalList.index(maxEval)
        tempPopulation.append(population[theIndex]) #每次迭代时先将上一代最大的个体放到下一代种群中
       # print("开始交叉")
        for i in range(personNum):
            #获得用于交叉的父母
            parentsFaIndex=getParents(evalList)
            parentsFa=population[parentsFaIndex]
            parentsMaIndex=getParents(evalList)
            parentsMa=population[parentsMaIndex]
            child=getCross(parentsFa,parentsMa,length)

            child=getVari(child,length)
            tempPopulation.append(child)
        population=tempPopulation
        flag+=1

        evalList = []
        for person in population:
            #print(person)
            evalList.append(evaluate(person,length))
        maxEval=max(evalList)

        if theBestEval<maxEval:
            theBestEval=maxEval
        theIndex = evalList.index(maxEval)
        person = population[theIndex]
        if person not in bestPerson:
            bestPerson.append(person)
            theScore.append(1)
        else:
            theScore[bestPerson.index(person)] += 1
        bestRecord.append(-theBestEval)
    # print('duration=',time.time()-timeStart)

    print(theScore)
    print(bestPerson)
    theBestEvalList=[]
    for item in bestPerson:
        theBestEvalList.append(evaluate(item,length))
    print(theBestEvalList)
    print(theBestEval)
    print(max(theScore))
    print(bestRecord)
    iterations=[i for i in range(iteration)]
    plt.plot(iterations, bestRecord, 'bo-')
    plt.title("Convergence curve")
    plt.show()
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值