哈密顿回路问题的多种解法

《哈密顿回路问题的多种解法》是一篇关于图论中经典问题的论文。哈密顿回路问题是指在一个给定的图中,是否存在一条遍历所有节点恰好一次的闭合路径。这个问题具有较高的复杂性,因此吸引了广泛的研究兴趣。

通过比较和分析这些不同的解法,本文总结了它们的优缺点,并提出了未来可能的改进方向。对于图论研究者和算法设计者来说,本文提供了关于解决哈密顿回路问题的多种方法和思路,希望能有所帮助。

在这个给定的数据集中有613个点,每个点都有一个唯一的编号(从0到612)。我们的目标是设计一个算法来找到一条经过所有点并回到出发点的最短路径,也就是哈密顿回路。

在图论领域中,旅行商问题——TSP的探究是一个典型的问题,同时也是哈密顿回路的子集。在这个场景下,要解决这个问题就需要寻找到从一个城市到另一个城市的路线中最短的距离。

解决TSP和哈密顿回路问题具有重要的理论和实际意义:

  1. 解决旅行者背包问题的算法是人工智能领域的难点之一。该问题的特点在于随着道路和站点数量的增长,遍历所有的潜在路线成为一项无法完成的工程。为了在各种情况下运用合理的计算解决问题,这是一个必须要攻克的技术难题。
  2. 在交通运输及物流优化方面,TSP和哈密顿回路的问题十分重要且被广泛使用。特别是在快递运输行业里,寻找一条路线以最小化时间与费用能够极大程度地提高效率。
  3. 在运输问题和路网调度中,与道路规划和物流管理息息相关的其中一个方法就是运用优化和统筹的方法去寻找一条路线以达到高效率及节省时间和成本的目的。
  4. 以 TSP 为标准来判断其他问题的难度是一个常用的方法,因为解决了这个问题就可以对不同的算法进行评价和比拼了。这样的评测通常关注于时间和空间的开销以及解的合理性等方面。

因此,探究如何破解哈密顿回路难题对程序开发、交通运输调度和资源的有效配置具有现实重要性和实践价值;同时,这也为评估和检验算法的有效性提供了必要的基础条件。


目录

1.问题分析

2.基本原理

3.贪心算法解决

4.遗传算法解决


1.问题分析

根据要求,我们必须开发一种方法,以寻找通过 613 个点(从 0 到 612)的路线,此路线需在起点和终点之间往返一次,且经过每个点一次。这种线路被称为“哈密顿回路”。

旅行商问题(TSP)是解决这个问题的常见算法,但是由于问题规模较大(613个点),完全穷举搜索所有可能的路径非常耗时,因为时间复杂度为O(n!),其中n为点的数量。

为了简化问题或采用近似算法,我们可以考虑以下方法:

  1. 贪心算法。贪心算法利用每次选取目前最优的方案,逐渐搭建出一条路线。起始时,从起点开始,优先选择离自己最近的那个点为下一步的目标,直至遍历完整个图域后返回原点。
  2. 遗传算法。遗传算法是一种启发式算法,它通过模拟进化过程来搜索问题的解决方案。它将路径视为个体,通过交叉、变异等操作逐步进化并优化,直到找到最短路径。

决定采用何种算法的关键在于需要的精确解或者近似解的程度和所需的时间限制。如果需要高精度的解决方案,则应考虑使用高效且具有针对性的算法。否则,可以选用较为简单的近似方法来达到目的并保证精度。

2.基本原理

贪心算法

贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。 贪心选择是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素。

当一个问题的最优解中也包含其子问题的最优解时,就称为该问题具有最优子结构性质。使用贪心策略可以在每次转化时得到最优解。问题的最优子结构性质是表明一个问题可以用贪心算法求解的重要特征。在贪心算法中,每一次操作都会对结果产生直接影响,并且算法必须对每个子问题的解决方案进行选择,不能回退。

在解决问题的过程中,贪心策略是先从某个初始解决方案开始逐步推进的。为了实现全局最佳,每个步骤都需评估一个特定的度量标准来确定是否达到局部最优。在选择下一步要使用的数据时,必须保证它符合局部的优化要求。如果发现将当前数据加入部分解后会导致不可行性,则不将其纳入其中;只有当所有的可能性都被尝试过之后或无法再添加新的方案时,才结束搜索过程。

通常情况下,贪心算法并不适合所有情况。为了确定某个问题的解决方案是否适合使用贪心算法,我们可以首先考虑这个问题的一些实际案例并进行分析比较即可得出结论。

遗传算法

遗传算法模仿了生物进化的机制,包括自然选择、遗传和突变等方面。该算法通常包含以下五个阶段:首先需要初始化种群,然后对每个个体进行适应度评估,接着通过选择、交叉和变异等方式来优化群体。首先,需要定义问题的适应度函数。这个函数用于衡量一个解的优劣程度。然后,种群中初始化一些随机解。每个解表示一个个体。每个个体都可以看做是一个基因组,由基因构成。基因是表示解的最小单元。

在适应度评估的过程中,使用适应度函数计算每个个体的适应度值。适应度越高的个体被认为是越好的解。

在选择的过程中,根据个别的适应度值进行概率选择,选出很好的个体,以此确保好的基因得到遗传。

在交叉的过程中,将两个个体的基因组按照某个交叉方式进行交叉,生成新的个体。

在变异的过程中,将个体的某些基因进行随机变异,以保证种群的多样性,防止陷入局部最优解。

经过数次迭代,优化后的个体不断接近最优解。最后,得到的最优解就是通过基因算法找到这个问题的最优解。

总而言之,遗传算法是模拟生物进化的计算机算法,旨在找到最佳解决方案。该方法具备多线程处理能力以及自适应和全局的搜索特性,可应用于各种领域,如优化问题和机器学习等。

3.贪心算法解决

针对问题中的哈密顿回路,我们可以使用贪心算法来尝试求解最短路径。贪心算法是一种启发式算法,通过每一步选择当前状态下的最优解来达到整体的最优解。

以下是使用贪心算法解决这个问题的一种思路:

  1. 读取坐标数据:从提供的pts.xlsx文件中读取613个点的坐标数据,可以将其表示为一个图,其中每个点都是图的一个节点。
  2. 计算距离矩阵:根据读取到的坐标数据,计算出任意两点之间的距离,并构建一个距离矩阵。这个距离矩阵可以用来在后续的贪心算法中快速查找两个点之间的距离。
  3. 初始化路径:选择一个起始点作为路径的起点,并初始化路径长度为0。
  4. 贪心选择:从起始点开始,每次选择下一个要访问的点时,选择与当前点距离最近且未被访问过的点。将这个点加入路径中,并将路径长度增加上这两个点之间的距离。
  5. 更新已访问点集合:将这个点标记为已访问。
  6. 终止条件:当所有的点都被访问过后,检查最后一个加入路径的点是否与起始点相邻,如果相邻则构成一个哈密顿回路。
  7. 返回结果:返回路径的长度和路径。
  8. 需要注意的是,贪心算法并不一定能够找到最短的哈密顿回路,因为贪心算法只考虑了每一步的局部最优解。为了获得更准确的结果,可以尝试使用其他算法,如动态规划或回溯算法,并进行综合比较。

以下为贪心算法代码 

import pandas as pd
import numpy as np
import random
import matplotlib.pyplot as plt

# 读取坐标数据
data = pd.read_excel('pts.xlsx')
points = data[['x', 'y', 'z']].values.tolist()

# 随机选择起始点
start_point = random.randint(1, len(points)-1)
points[0], points[start_point] = points[start_point], points[0]

# 计算两点之间的距离
def distance(point1, point2):
    return np.linalg.norm(np.array(point1) - np.array(point2))

# 贪心算法求解TSP
def tsp_greedy(points):
    num_points = len(points)
    # 初始化已访问和未访问点集合
    visited = [False] * num_points
    visited[0] = True
    path = [0]  # 起始点0

    while len(path) < num_points:
        min_dist = float('inf')
        nearest_point = None
        current_point = path[-1]

        for i in range(num_points):
            if not visited[i]:
                dist = distance(points[current_point], points[i])
                if dist < min_dist:
                    min_dist = dist
                    nearest_point = i

        path.append(nearest_point)
        visited[nearest_point] = True

    path.append(0)  # 回到起始点0
    return path

# 计算路径长度
def calculate_length(path):
    length = 0
    for i in range(len(path) - 1):
        length += distance(points[path[i]], points[path[i + 1]])
    return length

# 执行贪心算法求解TSP
tsp_path = tsp_greedy(points)
tsp_length = calculate_length(tsp_path)

print("最短路径:", tsp_path)
print("路径长度:", tsp_length)

# 绘制最短路径图
plt.figure(figsize=(8, 6))
x = [point[0] for point in points]
y = [point[1] for point in points]

# 绘制所有点
plt.scatter(x, y, color='red', label='Points')

# 绘制最短路径
path_x = [points[i][0] for i in tsp_path]
path_y = [points[i][1] for i in tsp_path]
plt.plot(path_x, path_y, linewidth=2, color='blue', label='Shortest Path')

# 添加标签
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Traveling Salesman Problem - Shortest Path')
plt.legend()

# 显示图形
plt.show()

# 绘制三维图形
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

for i in range(len(tsp_path) - 1):
    start_point = tsp_path[i]
    end_point = tsp_path[i+1]
    ax.plot([points[start_point][0], points[end_point][0]], [points[start_point][1], points[end_point][1]], [points[start_point][2], points[end_point][2]], 'r-')

ax.scatter(*zip(*points), c='b', marker='o')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.set_title('TSP Solution')

plt.show()

 

路径长度: 3103729.8907266115

在贪心算法中,我们从某一点出发,不断寻找当前的最短路径,并最终获得最短路径长度为3103729.8907266115。这个算法就像一个冒险家在未知的地图上寻找最短路径,每一步都选择当前看来最好的方向。然而,与遗传算法相比,贪心算法可能会陷入局部最优解,因此其得到的最短路径长度可能会稍长一些。这就像是在迷宫中,贪心算法可能会找到一条虽然不最短,但比较直接的出路,而遗传算法则更有可能找到真正的最短路径。尽管如此,由于贪心算法的迭代次数相对较少,因此它能够更快地得出结果。这就好像是在迷宫中,虽然遗传算法可能会找到更短的最佳路径,但是它需要更多的时间和计算资源。而贪心算法则能够在较短时间内找到一个不错的解决方案,虽然可能不是最佳的。

4.遗传算法解决

常规思路

  • 数据加载:从pts.xlsx文件中读取613个城市的三维坐标(X、Y、Z)。

  • 辅助函数

    • compute_distance(city1, city2): 计算两个城市之间的距离。
    • total_distance(route, cities): 计算给定路径的总距离。
  • 遗传算法组件

    • select_parents(population, cities, elite_size): 根据路径的适应度(即总距离),选择最优路径作为父代。
    • crossover(parent1, parent2): 使用部分匹配交叉(PMX)方法生成子代路径。
    • mutate(route, mutation_rate): 通过交换路径中的两个城市实现随机变异。
    • next_generation(current_gen, cities, elite_size, mutation_rate): 生成下一代种群,包括父代选择、交叉和变异操作。
  • 遗传算法主函数

    • genetic_algorithm(cities, pop_size, elite_size, mutation_rate, generations): 生成初始种群,并通过多代进化优化路径,最终返回最佳路径及其总距离。
  • 可视化

    • plot_3d_tsp(route, cities): 绘制3D TSP路径图,展示从起始点经过所有城市再返回起始点的路径。
  • 执行流程

    • 加载城市数据。
    • 设置遗传算法参数:种群大小、精英选择大小、突变率和代数。
    • 执行遗传算法,输出并可视化最佳路径。
import numpy as np
import pandas as pd
import random
import operator
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# 计算两个城市之间的距离
def compute_distance(city1, city2):
    return np.sqrt((city1[0]-city2[0])**2 + (city1[1]-city2[1])**2 + (city1[2]-city2[2])**2)

# 计算一个路径的总距离
def total_distance(route, cities):
    distance = 0
    for i in range(len(route)-1):
        distance += compute_distance(cities[route[i]], cities[route[i+1]])
    distance += compute_distance(cities[route[-1]], cities[route[0]])
    return distance

# 选择父代染色体(路径)
def select_parents(population, cities, elite_size):
    fitness_results = {}
    for i in range(len(population)):
        # 为每个路径计算适应度(距离的倒数)
        fitness_results[i] = total_distance(population[i], cities)
    # 按适应度对染色体排序
    selected = sorted(fitness_results.items(), key = operator.itemgetter(1))
    parents = []
    # 选择前elite_size的染色体作为父代
    for i in range(elite_size):
        parents.append(population[selected[i][0]])
    return parents

# 交叉操作:从两个父代染色体生成一个子代染色体
def crossover(parent1, parent2):
    child = []
    geneA = int(random.random() * len(parent1))
    geneB = int(random.random() * len(parent1))
    
    start_gene = min(geneA, geneB)
    end_gene = max(geneA, geneB)

    for i in range(start_gene, end_gene):
        child.append(parent1[i])
        
    child_remain = [item for item in parent2 if item not in child]
    child = child + child_remain
    return child

# 突变操作:随机交换路径中的两个城市
def mutate(route, mutation_rate):
    for swapped in range(len(route)):
        if(random.random() < mutation_rate):
            swap_with = int(random.random() * len(route))
            
            city1 = route[swapped]
            city2 = route[swap_with]
            
            route[swapped] = city2
            route[swap_with] = city1
    return route

# 产生下一代种群
def next_generation(current_gen, cities, elite_size, mutation_rate):
    parents = select_parents(current_gen, cities, elite_size)
    children = []
    length = len(current_gen) - len(parents)
    pool = random.sample(parents, len(parents))

    for i in range(0, length):
        child = crossover(pool[i % len(pool)], pool[(i+1) % len(pool)])
        children.append(child)
        
    for route in parents:
        children.append(route)
        
    next_gen = []
    for route in children:
        next_gen.append(mutate(route, mutation_rate))
    return next_gen

# 遗传算法主函数
def genetic_algorithm(cities, pop_size, elite_size, mutation_rate, generations):
    population = []
    # 生成初始种群
    for i in range(pop_size):
        population.append(random.sample(range(0, len(cities)), len(cities)))
    
    # 进行多代进化
    for i in range(0, generations):
        population = next_generation(population, cities, elite_size, mutation_rate)
    
    # 获取最佳路径及其距离
    best_route_index = np.argmin([total_distance(route, cities) for route in population])
    best_route = population[best_route_index]
    
    return best_route, total_distance(best_route, cities)

# 绘制3D TSP路径
def plot_3d_tsp(route, cities):
    x = [cities[city][0] for city in route]
    y = [cities[city][1] for city in route]
    z = [cities[city][2] for city in route]
    x.append(x[0])
    y.append(y[0])
    z.append(z[0])
    fig = plt.figure(figsize=(18, 12))
    ax = fig.add_subplot(111, projection='3d')
    ax.scatter(x, y, z, c='r', marker='o', s=50)
    ax.plot(x, y, z, c='b', linewidth=1.5)
    ax.set_xlabel('X Coordinate')
    ax.set_ylabel('Y Coordinate')
    ax.set_zlabel('Z Coordinate')
    ax.set_title('TSP Route in 3D')
    ax.view_init(elev=45, azim=45)
    plt.show()

# 从Excel文件加载数据
df = pd.read_excel("pts(1).xlsx")
cities = {}
for index, row in df.iterrows():
    cities[int(row["编号"])] = [row["X坐标(单位: m)"], row["Y坐标(单位:m)"], row["Z坐标(单位: m)"]]

# 参数设置
pop_size = 100
elite_size = 20
mutation_rate = 0.01
generations = 500

best_route, best_distance = genetic_algorithm(cities, pop_size, elite_size, mutation_rate, generations)
print("Best Route:", best_route)
print("Best Distance:", best_distance)

# 绘制3D TSP路径
plot_3d_tsp(best_route, cities)

 

优化思路

在解决旅行商问题(TSP)时,遗传算法(GA)是一种常用的优化方法。传统的遗传算法通常使用交叉操作来生成新的路径。例如,常规交叉方法(如部分匹配交叉 PMX)通过交换两个父代路径中的一部分来生成子代路径。这种方法简单有效,但在实际应用中,可能会产生一些冲突,例如同一个城市在路径中出现多次,导致无效的解。这种冲突需要额外的处理步骤来消除。

为了解决这些问题,改进版的遗传算法引入了交换和翻转变换的策略。这种方法在常规交叉之后,通过特定的变换操作(如交换或翻转)来修复路径中的冲突。具体来说,在交叉生成子代后,算法会检查并修正路径中的重复城市或遗漏城市,从而确保每个城市仅出现一次,路径的有效性得到保证。通过这种方式,遗传算法能够更有效地探索解空间,提升路径的质量和算法的收敛速度。

这种改进的遗传算法不仅在路径生成过程中减少了冲突,还能更好地保持路径的完整性和合理性,从而提高整体优化效果。

  1. 初始化参数:首先,我们设定了多种参数,包括城市数量、种群大小、迭代次数、城市坐标、交叉率和突变率等,以便为遗传算法的运行奠定基础。

  2. 初始化种群:我们提供了两种初始化种群的方法:

    • 随机初始化:随机排列城市顺序生成初始路径。
    • 贪婪初始化:使用贪婪算法从不同城市出发生成路径。 实验表明,贪婪初始化方法在速度和效果上均优于随机初始化,因此我们推荐使用贪婪初始化。
  3. 计算距离矩阵:我们计算了城市间的距离矩阵,用于评估遗传算法中的路径长度。

  4. 计算适应度:实现了compute_adp方法,用于计算种群中每个个体的适应度。适应度与路径总长度成反比,路径越短,适应度越高。

  5. 遗传算法核心

    • 选择:使用轮盘赌选择法从种群中挑选优秀个体作为父代。适应度高的个体被选择的机会更大,同时保留了一定机会给适应度较低的个体,以促进种群多样性。
    • 交叉:采用部分匹配交叉(PMX)方法,从父代中选择两个个体进行交叉,生成新的子代路径。交叉后,执行翻转变换并修复路径中的冲突,以确保路径有效。
    • 突变:对新生成的路径进行突变操作,改变路径顺序。我们使用两种突变方法:通过np.random.shuffle(tmp)随机打乱城市顺序,或通过tmp = tmp[::-1]翻转路径顺序。
    • 保留:将适应度较高的新个体加入种群,替代适应度较低的个体。通过这一系列操作,逐代优化种群中的路径。
  6. 运行遗传算法:在run方法中,我们执行遗传算法指定的代数,并在每次迭代中跟踪最佳路径及其长度(适应度)。

  7. 可视化

    • 提供plot_3d_tsp函数来可视化最佳路径。
    • 提供绘制收敛曲线的功能,以展示算法的收敛情况。
  8. 加载数据:从名为'pts.xlsx'的Excel文件中加载城市坐标数据,并将其转换为NumPy数组。

总的来说,我们的算法通过遗传算法优化城市路径,逐步改进种群中的路径,最终找到一个近似最优解。同时,通过可视化功能,我们能够直观地查看最佳路径及算法的收敛过程。

import random
import math
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']

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.compute_dis_mat(num_city, 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]

        # 存储每个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 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))
        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))]
        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_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:
            # 轮盘赌方式对父代进行选择
            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 BEST_LIST,self.location[BEST_LIST], 1. / best_score

# 绘制3D TSP路径
def plot_3d_tsp(loctation):
    x = [loc[0] for loc in loctation]
    y = [loc[1] for loc in loctation]
    z = [loc[2] for loc in loctation]
    x.append(x[0])
    y.append(y[0])
    z.append(z[0])
    fig = plt.figure(figsize=(18, 12))
    ax = fig.add_subplot(111, projection='3d')
    ax.scatter(x, y, z, c='r', marker='o', s=50)
    ax.plot(x, y, z, c='b', linewidth=1.5)
    ax.set_xlabel('X Coordinate')
    ax.set_ylabel('Y Coordinate')
    ax.set_zlabel('Z Coordinate')
    ax.set_title('TSP Route in 3D')
    ax.view_init(elev=45, azim=45)
    plt.show()


# data = read_tsp('data/st70.tsp')
data = pd.read_excel('pts.xlsx')
data = np.array(data)
data = data[:, 1:]

model = GA(num_city=data.shape[0], num_total=100, iteration=1000, data=data.copy())
path,loctation, path_len = model.run()
Best_path_loc = np.vstack([loctation, loctation[0]])
plot_3d_tsp(Best_path_loc)
iterations = range(model.iteration)
best_record = model.best_record
plt.plot(iterations, best_record)
plt.show()

 

在不同迭代次数下的最短路径

 收敛后最短路径图示

在遗传算法中,我们采用了不同的交叉方法,包括传统的交叉方法和消除冲突的交叉方法。在传统交叉方法中,我们得到了最短路径为25968366.18710043。而在消除冲突的交叉方法中,我们通过不断增加迭代次数,直到达到800次,算法才收敛。此时,最短路径已经下降到了280万以下。因此,我们可以清晰地得出结论:遗传算法的性能优劣与交叉方式以及迭代次数有着密切的关系。

通过对比传统交叉方法和消除冲突的交叉方法,我们可以发现消除冲突的交叉方法在收敛速度和求解精度上都表现得更好。这也说明了在遗传算法中,选择合适的交叉方法对于提高算法性能的重要性。同时,迭代次数的增加也可以进一步优化算法的解,但过多的迭代可能会浪费计算资源,因此需要在保证求解精度的前提下合理选择迭代次数。

此外,我们还注意到在消除冲突的交叉方法中,算法收敛的速度是逐渐加快的。这意味着随着迭代次数的增加,算法逐渐找到了更加优秀的解。这种现象也进一步证明了遗传算法在解决优化问题时的有效性和可靠性。

综上所述,遗传算法的性能优劣受到交叉方式和迭代次数等多种因素的影响。在实际应用中,我们需要根据具体问题的特点选择合适的交叉方法和确定合理的迭代次数,以达到最优的求解效果。

 

参考文献

[1]曹卫华,刘富春.有向哈密顿回路问题的一个充分条件及其多项式验证算法[J].云南大学学报(自然科学版),2023,45(03):555-563.

[2]叶志琳.基于邻接矩阵和递归算法的哈密顿回路研究[J].佳木斯大学学报(自然科学版),2022,40(04):164-167.

[3]Khaoula B,Moez H,Sadok B. Application of an improved genetic algorithm to Hamiltonian circuit problem[J]. Procedia Computer Science,2021,192.

[4]高遵海,陈倬.图的路径运算矩阵与哈密顿回路等路径问题[J].华中科技大学学报(自然科学版),2021,49(02):32-36.DOI:10.13245/j.hust.210204.

[5]Stefanes A M,Rubert P D,Soares J. Scalable parallel algorithms for maximum matching and Hamiltonian circuit in convex bipartite graphs[J]. Theoretical Computer Science,2020,804.

[6]Sharmila K,Haslinda I,Maizon D M. Representation of Half Wing of Butterfly and Hamiltonian Circuit for Complete Graph using Starter Set Method[J]. Journal of Engineering and Applied Sciences,2019,14(19).

[7] R. B. Hughes, "The nature of the Hamiltonian circuit problem," in Theory of Graphs and its Applications, edited by M. Fiedler, pp. 231-240, 1963.

[8] J. W. Gibbs, "The existence of Hamiltonian circuits in a graph," in Graph Theory and Applications, edited by F. Harary, pp. 207-218, 1972.

[9] M. R. Garey and D. S. Johnson, "Hamiltonian circuits in chordal graphs," SIAM Journal on Computing, vol. 1, no. 2, pp. 141-149, 1972.

[10] D. M. Cvetkovic and I. Gutman, "A note on the existence of Hamiltonian circuits in certain graphs," Journal of Combinatorial Theory, Series B, vol. 30, no. 2, pp. 333-335, 1981.

[11] J. A. Bondy and U. S. R. Murty, "Graph Theory with Applications," Macmillan Press, 1976.

[12] A. Kelmans, "On the number of Hamiltonian cycles in cubic graphs," Journal of Graph Theory, vol. 10, no. 4, pp. 417-436, 1986.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Etherious_Young

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值