蚁群优化算法ACO

2023-12-14 19:04:48

蚁群优化算法模拟了自然界中蚂蚁的觅食行为,信息素浓度的大小表征路径的远近,信息素浓度越高,表示对应的路径距离越短。同时,路径上的信息素浓度会随着时间的推进而逐渐衰减。

1.过程

(1)初始化参数

蚁群规模、信息素因子α、启发函数因子β、信息素挥发因子ρ、信息素释放总量q、最大迭代次数等。

(2)构建解空间

起初把蚂蚁随机放到不同城市,对于每一个蚂蚁,采用轮盘赌找出下一个要访问的城市,直到访问完所有城市。

(3)更新信息素

计算各个蚂蚁经过的路径长度,找出本次迭代中的最短路径,并更新城市路径的信息素浓度。更新信息素浓度分为三种策略:蚁周、蚁量、蚁密。

蚁周是完成一次路径循环后,蚂蚁才释放信息素。

蚁量是一只蚂蚁从一个城市到达另一个城市后,直接释放信息素。

蚁密是一只蚂蚁从一个城市到达另一个城市后,释放的信息素需要除以城市间的路径距离。

(4)判断是否终止

迭代次数到达最大迭代次数后,终止。否则,回到(2)。

2.流程图

3.代码

from utils import draw_picture, get_next_pos, init_pos, save_best_result
import tqdm

import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('TkAgg')


class ACO(object):
    def __init__(self, ant_count: int, generations: int, alpha: float, beta: float, rho: float, q: int,
                 strategy: int, distance, points):
        """
        :param ant_count:
        :param generations:
        :param alpha: relative importance of pheromone
        :param beta: relative importance of heuristic information
        :param rho: pheromone residual coefficient
        :param q: pheromone intensity
        :param strategy: pheromone update strategy. 0 - ant-cycle, 1 - ant-quality, 2 - ant-density
        :param distance: distance between each points
        """
        self.Q = q
        self.rho = rho
        self.beta = beta
        self.alpha = alpha
        self.ant_count = ant_count
        self.generations = generations
        self.update_strategy = strategy
        self.points = points
        self.distance = distance
        # 路径数目
        self.rank = len(distance)
        #
        self.eta = [[0 if i == j else 1 / distance[i][j] for j in range(self.rank)] for i in
                    range(self.rank)]
        # 每条路径上的信息素浓度
        self.pheromone_content = [[1 for _ in range(self.rank)] for _ in range(self.rank)]

    # 初始化
    def initialization(self):
        self.memory_vector = []     # 记录每个蚂蚁的总路径
        for _ in range(self.ant_count):     # 不关心i,可以用_代替i
            self.memory_vector.append([init_pos(self.rank)])    # 随机初始化每个蚂蚁位置

    # 用轮盘赌计算蚂蚁要去的下一个城市
    def roulette(self, id, pos):
        possibility = []
        for i in range(self.rank):
            if i in self.memory_vector[id]:
                # 如果蚂蚁访问过这个城市,则不去
                possibility.append(0)
            else:
                possibility.append(self.pheromone_content[pos][i]**self.alpha*(self.eta[pos][i]**self.beta))
        next_pos = get_next_pos(possibility)
        return next_pos

    # 增加信息素浓度:三种策略,这里还有点问题
    def update_pheromone_delta(self, ant_path):
        if self.update_strategy == 0:
            for i in range(self.ant_count):
                self.pheromone_content[ant_path[i][0]][ant_path[i][1]] += self.Q
                self.pheromone_content[ant_path[i][1]][ant_path[i][0]] += self.Q
                if len(self.memory_vector[0]) == self.rank:
                    self.pheromone_content[self.memory_vector[i][-1]][self.memory_vector[i][0]] += self.Q
                    self.pheromone_content[self.memory_vector[i][0]][self.memory_vector[i][-1]] += self.Q
        elif self.update_strategy == 1:
            for i in range(self.ant_count):
                self.pheromone_content[ant_path[i][0]][ant_path[i][1]] += (self.Q/self.distance[ant_path[i][0]][ant_path[i][1]])
                self.pheromone_content[ant_path[i][1]][ant_path[i][0]] += (self.Q/self.distance[ant_path[i][0]][ant_path[i][1]])
                if len(self.memory_vector[0]) == self.rank:
                    self.pheromone_content[self.memory_vector[i][-1]][self.memory_vector[i][0]]+=self.Q/self.distance[self.memory_vector[i][0]][self.memory_vector[i][-1]]
                    self.pheromone_content[self.memory_vector[i][0]][self.memory_vector[i][-1]]+=self.Q/self.distance[self.memory_vector[i][0]][self.memory_vector[i][-1]]
        elif self.update_strategy == 2:
            # 完成一次循环后
            if len(self.memory_vector[0]) == self.rank:
                # 计算每个蚂蚁本次的总路程
                total_cost = []
                for i in range(self.ant_count):
                    cost = 0
                    for j in range(1, self.rank):
                        cost += self.distance[self.memory_vector[i][j-1]][self.memory_vector[i][j]]
                    cost += self.distance[self.memory_vector[i][0]][self.memory_vector[i][-1]]
                    total_cost.append(cost)
                # 更新信息素浓度
                for i in range(self.ant_count):
                    delta = self.Q/total_cost[i]
                    for j in range(1, self.rank):
                        # 双向路径
                        self.pheromone_content[self.memory_vector[i][j-1]][self.memory_vector[i][j]] += delta
                        self.pheromone_content[self.memory_vector[i][j]][self.memory_vector[i][j-1]] += delta
                    # 蚂蚁最初的那条路
                    self.pheromone_content[self.memory_vector[i][0]][self.memory_vector[i][-1]] += delta
                    self.pheromone_content[self.memory_vector[i][-1]][self.memory_vector[i][0]] += delta
            else:
                # 没有完成一次循环
                pass
        else:
            raise KeyError

    # 减少信息素浓度
    def update_pheromone(self):
        for i in range(self.rank):
            for j in range(self.rank):
                self.pheromone_content[i][j] = self.pheromone_content[i][j] * (1 - self.rho)

    # 更新蚂蚁本次迭代找到的路径
    def update_memory_vector(self, ant_path):
        for i in range(self.ant_count):
            self.memory_vector[i].append(ant_path[i][1])

    # 执行算法
    def run(self):
        self.cost = 0
        self.path = []
        plt.ion()   # 启用交互模式(动态图)
        # tqdm进度条
        for iteration in tqdm.tqdm(range(self.generations), desc='Processing'):
            # print(f'-----start iteration {iteration+1} of ACO-----')
            self.initialization()
            for steps in range(self.rank - 1):
                # 在一次新的迭代中,蚂蚁选择一条路径从pos到next_pos
                ant_path = []
                for i in range(self.ant_count):     # 对于每一只蚂蚁
                    pos = self.memory_vector[i][-1]
                    next_pos = self.roulette(i, pos)
                    ant_path.append([pos, next_pos])
                self.update_memory_vector(ant_path)     # 更新蚂蚁本次迭代的路径
                self.update_pheromone_delta(ant_path)   # 增加路径上的信息素浓度
            self.update_pheromone()     # 减少信息素浓度
            plt.cla()
            plt.title("ant colony algorithm")
            self.cost, self.path = draw_picture(self.points, self.distance, self.memory_vector, iteration)
            plt.pause(0.01)     # 暂停运行一段时间,能将内存中的图像显示出来

    # 保存数据
    def save(self, seed):
        save_best_result(self.path, self.points, seed)
        plt.ioff()  # 关闭交互模式
        plt.show()  # 显示图片,会阻塞后面代码运行,适用于静态图

# 开发时间:2023/12/13 19:50
import random
import math
import numpy as np

import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('TkAgg')

def random_init(points_num ,min_x ,max_x, min_y, max_y):
    points = []

    while len(points) != points_num:
        x = random.randint(min_x, max_x)
        y = random.randint(min_y, max_y)
        if [x, y] in points:
            continue
        points.append([x, y])

    file = open("temp.txt", "w")
    for i in range(len(points)):
        file.write(f'{points[i][0]}{points[i][1]}\n')
    file.close()
    print("Data running time was saved in file [temp.txt]")
    return points

def dis(point1, point2):
    return math.sqrt((point1[0]-point2[0]) ** 2 + (point1[1]-point2[1]) ** 2)

def calculate_distance(points):
    distance = []
    for i in range(len(points)):
        list = []
        for j in range(len(points)):
            list.append(dis(points[i], points[j]))
        distance.append(list)
    return distance

def get_next_pos(possibility):
    n = sum(possibility)

    for i in range(len(possibility)):
        possibility[i] /= n

    n = sum(possibility)
    r = random.uniform(0, n)
    pos = 0
    while True:
        if possibility[pos] == 0:
            pos += 1
        elif r-possibility[pos] < 0:
            return pos
        else:
            r -= possibility[pos]
            pos += 1

def init_pos(rank):
    pos = random.randint(0, rank-1)
    return pos

def load_example(text_name):
    file = open(text_name, 'r')
    content = file.readlines()
    points = []
    for data in content:
        x, y = data.split(" ")
        points.append([int(x), int(y)])
    distance = calculate_distance(points)

    return points, distance

def draw_picture(points, distance, path, iteration):
    rank = len(points)
    ant_number = len(path)
    x = []
    y = []
    for i in range(rank):
        x.append(points[i][0])
        y.append(points[i][1])
    plt.scatter(x, y)

    min_cost = np.inf
    for i in range(ant_number):
        temp_cost = 0
        for j in range(1, rank):
            temp_cost += distance[path[i][j-1]][path[i][j]]
        temp_cost += distance[path[i][0]][path[i][-1]]
        if temp_cost < min_cost:
            min_cost = temp_cost
            best_path = path[i]

    for i in range(ant_number):
        for j in range(rank):
            x[j] = points[path[i][j]][0]
            y[j] = points[path[i][j]][1]
        plt.plot(x, y)

    plt.text(0, 0, f'iteration:{iteration} min_cost = {round(min_cost, 2)}', family='fantasy', fontsize=12,style='italic',color='mediumvioletred')
    return round(min_cost, 2), best_path

def save_best_result(path, points, seed):
    for i in range(1, len(path)):
        x1 = points[path[i - 1]][0]
        y1 = points[path[i - 1]][1]
        x2 = points[path[i]][0]
        y2 = points[path[i]][1]
        plt.arrow(x1, y1, x2 - x1, y2 - y1, width=0.05, color='r', length_includes_head=True)
    plt.arrow(x2, y2, points[path[0]][0] - x2, points[path[0]][1] - y2, width=0.05, color='r',
              length_includes_head=True)
    plt.gcf().set_size_inches(20, 12)  # get current figure
    plt.savefig("result.png")
    print("画图呀")
    plt.close()
    print('\n')
    print("-" * 50)
    print(f"[Best Path](random seed [{seed}])")
    print(show_path(path))
    print("Last result picture was saved in [result.png]")
    print(f"If you want to get this result again please add '-s {seed}'")
    print("-" * 50)
    print('\n')

def show_path(path):
    route = str(path[0])
    for i in range(1, len(path)):
        route = route + "->"+str(path[i])
    route = route + "->"+str(path[0])
    return route
import argparse
from ACO import ACO
import random
from utils import random_init,calculate_distance,load_example

import matplotlib
matplotlib.use('TkAgg')


def default_argument_parser():
    # ArgumentParser 编写命令行接口
    # 创建对象(解析器)
    parser = argparse.ArgumentParser(description="ant colony algorithm")
    # 添加参数
    parser.add_argument("--test", nargs="?")  # 参数可设置0或1个
    parser.add_argument('--ant', default=5, type=int)   # 蚂蚁数
    parser.add_argument('--points', default=20, type=int)   # 城市数
    parser.add_argument('--generation', default=50,type=int)  # 迭代次数
    parser.add_argument('--alpha', default=2.0,type=float)     # 信息素因子
    parser.add_argument('--beta', default=3.0,type=float)     # 启发函数因子
    parser.add_argument('--rho', default=0.5,type=float)       # 信息素挥发因子
    parser.add_argument('--q', default=100,type=float)      # 信息素浓度
    parser.add_argument('--strategy', default=2,type=int)   # 信息素更新策略
    parser.add_argument('--min_x', default=0,type=int)      # 范围
    parser.add_argument('--max_x', default=100,type=int)
    parser.add_argument('--min_y', default=0,type=int)
    parser.add_argument('--max_y', default=100,type=int)
    parser.add_argument('-s', '--seed', type=int)
    '''
     信息素三种更新策略
     0:  蚁量:加浓度
     1:  蚁密:加浓度/城市间的路径距离
     2:  蚁周:蚂蚁全走完再更新信息素浓度
     '''
    return parser


def main():
    # 解析参数
    args = default_argument_parser().parse_args()
    if(args.seed == None):
        seed = random.randint(1, 10000)
        random.seed(seed)   # 随机数种子
        print("no random seed found")
        args.seed = seed
    else:
        print(f"set random seed {args.seed}")
        random.seed(args.seed)
    if args.test != None:
        points, distance = load_example(args.test)
    else:
        points = random_init(args.points, args.min_x, args.max_x, args.min_y, args.max_y)
        distance = calculate_distance(points)
    aco = ACO(
        ant_count=args.ant,
        generations=args.generation,
        alpha=args.alpha,
        beta=args.beta,
        rho=args.rho,
        q=args.q,
        strategy=args.strategy,
        points=points,
        distance=distance,
    )
    aco.run()       # 执行算法
    aco.save(args.seed)     # 保存数据

# 当.py文件被直接运行时,下面代码会执行;当.py文件以模块形式被导入则不会执行
if __name__ == '__main__':
    main()

4.优缺点

优点:采用正反馈机制进行更新,使得结果不断收敛;可以并行计算。

缺点:收敛速度慢;不适用于解空间是连续的优化问题。

文章来源:https://blog.csdn.net/qq_51165184/article/details/134980423
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。