基于遗传算法 (Genetic Algorithm, GA) 实现N个城市环游最优路径规划计算

遗传算法(Genetic Algorithm,GA)是一种基于生物进化过程的优化算法,通过模拟自然界的遗传和进化机制,寻找问题的最优解。在解决复杂和多变量的优化问题中,遗传算法表现出良好的鲁棒性和全局搜索能力。

遗传算法的流程包括以下步骤:

  1. 初始化种群:创建由个体组成的初始种群。每个个体表示问题的一个潜在解,并用染色体(Chromosome)表示。

  2. 评估适应度:通过适应度函数评估种群中每个个体的适应度,即个体解的优劣程度。适应度函数根据问题不同而定,可以是目标函数值的最大化或最小化。

  3. 选择操作:根据适应度值,选择一部分适应度较高的个体作为“父代”,用于产生下一代。高适应度个体被选择的概率较高,以保留良好特性。

  4. 交叉操作:从父代中选择两个个体,通过交叉操作生成新的个体。交叉操作模拟了基因在生物繁殖过程中的交叉。

  5. 变异操作:对新生成的个体进行变异,以增加种群的多样性。变异操作模拟了基因突变过程。

  6. 更新种群:用新生成的个体替换原有的个体,形成下一代种群。

  7. 终止条件:重复进行步骤2到步骤6,直到满足终止条件,如达到最大迭代次数或达到特定的目标解。

遗传算法的优点包括:

  1. 全局搜索能力:遗传算法通过维护一个多样性的种群,能够在解空间中进行全局搜索,找到更优解。

  2. 鲁棒性:遗传算法不依赖于问题的具体形式,适用于多种优化问题。它可以处理连续、离散或混合型的变量,并且对问题的约束灵活适应。

  3. 并行计算能力:由于遗传算法是通过对个体进行并行评估和操作,它能够有效利用并行计算能力,加速优化过程。

然而,遗传算法也存在一些缺点:

  1. 参数设置:遗传算法的性能表现高度依赖于参数的设置,如种群大小、交叉率、变异率等。不同的问题可能需要不同的参数设置。

  2. 收敛速度:由于遗传算法通过随机搜索进行优化,它的收敛速度相对较慢。对于复杂问题,可能需要较长的时间才能找到较好的解。

  3. 问题依赖性:尽管遗传算法适用于多种问题,但对于特定的问题,可能存在更适合的优化算法。

遗传算法是一种强大的优化算法,能够解决复杂的优化问题,并具有全局搜索能力和鲁棒性。但它也有一些限制,对参数设置敏感,收敛速度较慢。在实践中,可以根据具体问题的特点和限制,选择合适的优化算法进行求解。

早上的时候遇上一个问题就是想要能够自动化地求解N个地点遍历最优的路径规划,这时候想到了使用遗传算法GA,GA可以说是比较早比较经典的算法了,上面对其进行了简单的总结和回顾,这里,我们来看下具体的实现。

为了方便说明,这里简化问题,数据都是随机生成的,当然了你也可以实际使用地图获取真实的城市坐标,都是可以的:

# 城市坐标
city_pos_list = np.random.rand(config.city_num, config.pos_dimension)
print("city_pos_list: ", city_pos_list)

接下来需要构建计算城市两两之间的距离,形成距离矩阵,如下所示:

def build_dist_mat(data):
    """
    构建距离矩阵
    """
    n = config.city_num
    dist_mat = np.zeros([n, n])
    for i in range(n):
        for j in range(i + 1, n):
            d = data[i, :] - data[j, :]
            dist_mat[i, j] = np.dot(d, d)
            dist_mat[j, i] = dist_mat[i, j]
    return dist_mat

我们这里设定城市数量为5,看下实例结果输出:

city_pos_list:  
[[0.4857694  0.37717288]
 [0.12982822 0.92253033]
 [0.63096373 0.89184198]
 [0.70368269 0.59460617]
 [0.8681248  0.58574649]]


city_dist_mat:
[0.0, 0.4241088699265403, 0.2859656753224421, 0.09476343738540502, 0.18969860731199]
[0.4241088699265403, 0.0, 0.25207857105384973, 0.43684320650858754, 0.6585051993116404]
[0.2859656753224421, 0.25207857105384973, 0.0, 0.09363717469660143, 0.1499398246120906]
[0.09476343738540502, 0.43684320650858754, 0.09363717469660143, 0.0, 0.02711970301160461]
[0.18969860731199, 0.6585051993116404, 0.1499398246120906, 0.02711970301160461, 0.0]

GA算法的实现可以直接参考开源社区即可,网上有很多非常好的讲解,这里我给出自己的实例实现,如下所示:

class Individual:
    """
    个体类
    """
    def __init__(self, genes=None):
        # 随机生成序列
        if genes is None:
            genes = [i for i in range(gene_len)]
            random.shuffle(genes)
        self.genes = genes
        self.fitness = self.evaluate_fitness()

    def evaluate_fitness(self):
        # 计算个体适应度
        fitness = 0.0
        for i in range(gene_len - 1):
            # 起始城市和目标城市
            from_idx = self.genes[i]
            to_idx = self.genes[i + 1]
            fitness += city_dist_mat[from_idx, to_idx]
        # 连接首尾
        fitness += city_dist_mat[self.genes[-1], self.genes[0]]
        return fitness


class Ga:
    def __init__(self, input_):
        global city_dist_mat
        city_dist_mat = input_
        self.best = None  # 每一代的最佳个体
        self.individual_list = []  # 每一代的个体列表
        self.result_list = []  # 每一代对应的解
        self.fitness_list = []  # 每一代对应的适应度

    def cross(self):
        new_gen = []
        random.shuffle(self.individual_list)
        for i in range(0, individual_num - 1, 2):
            # 父代基因
            genes1 = copy_list(self.individual_list[i].genes)
            genes2 = copy_list(self.individual_list[i + 1].genes)
            index1 = random.randint(0, gene_len - 2)
            index2 = random.randint(index1, gene_len - 1)
            pos1_recorder = {value: idx for idx, value in enumerate(genes1)}
            pos2_recorder = {value: idx for idx, value in enumerate(genes2)}
            # 交叉
            for j in range(index1, index2):
                value1, value2 = genes1[j], genes2[j]
                pos1, pos2 = pos1_recorder[value2], pos2_recorder[value1]
                genes1[j], genes1[pos1] = genes1[pos1], genes1[j]
                genes2[j], genes2[pos2] = genes2[pos2], genes2[j]
                pos1_recorder[value1], pos1_recorder[value2] = pos1, j
                pos2_recorder[value1], pos2_recorder[value2] = j, pos2
            new_gen.append(Individual(genes1))
            new_gen.append(Individual(genes2))
        return new_gen

    def mutate(self, new_gen):
        for individual in new_gen:
            if random.random() < mutate_prob:
                # 翻转切片
                old_genes = copy_list(individual.genes)
                index1 = random.randint(0, gene_len - 2)
                index2 = random.randint(index1, gene_len - 1)
                genes_mutate = old_genes[index1:index2]
                genes_mutate.reverse()
                individual.genes = old_genes[:index1] + genes_mutate + old_genes[index2:]
        # 两代合并
        self.individual_list += new_gen

    def select(self):
        # 筛选
        group_num = 10  # 小组数
        group_size = 10  # 每小组人数
        group_winner = individual_num // group_num  # 每小组获胜人数
        winners = []  # 锦标赛结果
        for i in range(group_num):
            group = []
            for j in range(group_size):
                # 随机组成小组
                player = random.choice(self.individual_list)
                player = Individual(player.genes)
                group.append(player)
            group = Ga.rank(group)
            # 取出获胜者
            winners += group[:group_winner]
        self.individual_list = winners

    @staticmethod
    def rank(group):
        # 冒泡排序
        for i in range(1, len(group)):
            for j in range(0, len(group) - i):
                if group[j].fitness > group[j + 1].fitness:
                    group[j], group[j + 1] = group[j + 1], group[j]
        return group

    def next_gen(self):
        # 交叉
        new_gen = self.cross()
        # 变异
        self.mutate(new_gen)
        # 选择
        self.select()
        # 获得这一代的结果
        for individual in self.individual_list:
            if individual.fitness < self.best.fitness:
                self.best = individual

    def train(self):
        # 初代种群
        self.individual_list = [Individual() for _ in range(individual_num)]
        self.best = self.individual_list[0]
        # 迭代
        for i in range(gen_num):
            self.next_gen()
            # 连接首尾
            result = copy_list(self.best.genes)
            result.append(result[0])
            self.result_list.append(result)
            self.fitness_list.append(self.best.fitness)
        return self.result_list, self.fitness_list

接下来就可以喂入数据执行算法计算了:

# 遗传算法运行
ga = Ga(city_dist_mat)
result_list, fitness_list = ga.train()

为了直观呈现效果,这里对其结果进行了可视化,如下所示:

【5个城市】

【10个城市】

【15个城市】

【20个城市】

【30个城市】

【50个城市】

【100个城市】

随着城市数量的增多,路线显得比较混乱,不过还是能看出来轮廓的,感兴趣的话都可以自行动手尝试一下。

猜你喜欢

转载自blog.csdn.net/Together_CZ/article/details/132581075