1. 程式人生 > >有限級資訊素蟻群演算法

有限級資訊素蟻群演算法

有限級資訊素蟻群演算法使用路徑等級作為資訊素更新的依據,相比於傳統的蟻群演算法,捨棄了目標函式作為資訊素更新的依據這一方法。在TSP問題中,目標函式實際就是路徑長度,在傳統的蟻群演算法中資訊素更新量為Q/f(x),其中Q為某一常數,而f(x)就是目標函式值,即路徑長度。而在有限級資訊素蟻群演算法中在這一點做出了改變。

首先給出演算法的基本步驟:

步驟1:設定初始引數,初始化資訊素

步驟2:按照路徑選擇規則構造問題的解

步驟3:按照資訊素更新規則更新資訊素

步驟4:判斷是否達到停止條件,若滿足,則停止迭代,否則跳至步驟2

有限級資訊素蟻群演算法主要變動就在步驟3,記h(i,j)為弧(i,j)的級別,g(x)

是一個單調遞增函式,用於將弧的級別對映為資訊素。在有限級資訊素蟻群演算法中,弧級別越大則弧上的資訊素也就越高。r_1表示路徑懲罰等級,r_2表示路徑獎勵等級。假設路徑最小等級為1,路徑最大等級為M\widehat{w}為當前最優解,w_t為當前次迭代最優解。

步驟3可以細分為以下幾步:

<1>.\forall (i,j):h(i,j)\leftarrow h(i,j)-r_1

<2>.如果f(\widehat{w})> f(w_t),則\widehat{w}=w_t

<3>.對於\widehat{w}

\forall (i,j)\in \widehat{w},h(i,j)\leftarrow h(i,j)+r_2,即將當前最優解路徑上的所有弧等級加r_2

<4>.\forall (i,j):h(i,j)\leftarrow max(1, h(i, j)),保證弧等級最小為1

<5>.\forall (i,j):h(i,j)\leftarrow min(M, h(i, j)),保證弧等級最大為M

<6>.\forall (i,j):\tau (i,j)=g(h(i,j))g(x)通常選取為\frac{\tau_{max} - 1}{M - 1}(x-1)+1或者\sqrt{\frac{\tau_{max}^2 - 1}{M - 1}(x-1)+1}

有限級資訊素蟻群演算法程式碼如下:


# coding: utf-8

# In[8]:


import numpy as np
from numpy import random as rd
import os
import matplotlib.pyplot as plt
np.set_printoptions(linewidth=1000, suppress=True)


# In[2]:

# 資料下載地址:https://www.iwr.uni-heidelberg.de/groups/comopt/software/TSPLIB95/tsp/
# 將資料存放在在程式碼存放目錄的tspdata資料夾內
with open("./tspdata/kroA100.tsp", "r", encoding="utf-8") as file:
    data = file.read()
data = np.array([p.split(" ")[1:] for p in data.split("\n")[6:-2]]).astype(np.float32)
data


# In[3]:


def calc_distance_matrix(city_position_matrix):
    """
        city_position_matrix:城市座標矩陣,形狀為N行2列,N為城市數
        返回城市間距離矩陣distance_matrix,假設城市i到城市j的距離等於城市j到城市i的距離,因此,distance_matrix為上三角矩陣
    """
    city_number = city_position_matrix.shape[0]
    distance_matrix = np.zeros(shape=[city_number, city_number], dtype=np.float32)
    for i in range(city_number):
        from_city_position = city_position_matrix[i]
        for j in range(i + 1, city_number):
            to_city_position = city_position_matrix[j]
            distance_matrix[i, j] = np.sqrt(np.sum(np.power(city_position_matrix[i] - city_position_matrix[j], 2)))
    return distance_matrix


# In[4]:


class FGP_ACO(object):
    
    def __init__(self, initial_pheromone, r_2, r_1, M, beta, max_pheromone, run_times, city_position_matrix, ant_count, initial_route_level):
        """
            initial_pheromone:每條路徑上的資訊素初始值
            r_2:路徑獎勵等級
            r_1:路徑懲罰級別
            M:路徑最大級別
            beta:啟發資訊重要程度
            max_pheromone:資訊素最大值
            run_times:最大迴圈次數
            city_position_matrix:城市座標矩陣,形狀為N行2列,N為城市數
            ant_count:螞蟻數量
            initial_route_leve:表示每條路徑的初始級別
        """
        self.r_2 = r_2
        self.r_1 = r_1
        self.M = M
        self.beta = beta
        self.max_pheromone = max_pheromone
        self.run_times = run_times
        self.city_position_matrix = city_position_matrix
        self.ant_count = ant_count
        # 城市間距離矩陣
        self.distance_matrix = calc_distance_matrix(city_position_matrix)
        # 城市數量
        self.N = city_position_matrix.shape[0]
        # 啟發資訊矩陣
        self.heuristic_information_matrix = np.triu(1 / self.distance_matrix, k=1)
        self.pheromone_matrix = np.triu(initial_pheromone * np.ones_like(self.distance_matrix), k=1)
        # 路徑級別矩陣
        self.route_level_matrix = np.triu(np.ones_like(self.distance_matrix) * initial_route_level, k=1)
        # 每一代的最優距離列表
        self.optimal_distance_of_every_iteration = []
        # 每一代的當前最優距離列表
        self.current_optimal_distance_of_every_iteration = []
    
    def calc_move_prob_matrix(self, passed_city, not_passed_city):
        """
            passed_city:每隻螞蟻已經經過的城市
            not_passed_city:每隻螞蟻未經過的城市
            返回未經過城市中每個城市被選擇的概率矩陣move_prob_matrix,形狀為[self.ant_count, np.array(not_passed_city).shape[1]]
        """
        move_prob_matrix = []
        for i in range(self.ant_count):
            # 由於都是上三角矩陣,因此,如果路徑為i到j,且i > j的,需要對掉,變為j到i
            start = np.array([passed_city[i][-1]] * len(not_passed_city[i]))
            end = np.array(not_passed_city[i])
            bool_index = start > end
            start[bool_index] = end[bool_index]
            end[bool_index] = passed_city[i][-1]
            # 找出每條未經過路徑上的資訊素not_passed_route_pheromone以及啟發資訊not_passed_route_heuristic_information,
            # 其長度均為未經過城市的個數
            not_passed_route_pheromone = self.pheromone_matrix[start, end]
            not_passed_route_heuristic_information = self.heuristic_information_matrix[start, end]
            move_prob_matrix.append(not_passed_route_pheromone * not_passed_route_heuristic_information ** self.beta / np.sum(not_passed_route_pheromone * not_passed_route_heuristic_information ** self.beta))
        return np.array(move_prob_matrix)
    
    def calc_route_distance(self, route):
        """
            route:表示一次迴圈的路徑,例如[0, 2, 1, 4, 3],表示0->2->1->4->3->0
            返回這條路徑的長度
        """
        distance = 0
        for i in range(len(route) - 1):
            min_value, max_value = sorted([route[i], route[i + 1]])
            distance += self.distance_matrix[min_value, max_value]
        min_value, max_value = sorted([route[0], route[1]])
        distance += self.distance_matrix[min_value, max_value]
        return distance
    
    def g_function(self):
        """
            更新資訊素矩陣
        """
        self.pheromone_matrix = np.sqrt((self.max_pheromone ** 2 - 1) / (self.M - 1) * (self.route_level_matrix - 1) + 1)
        
    def run(self):
        """
            迭代尋優,返回最優路徑以及最優路徑長度
        """
        # 隨即初始化一個當前最優解, current_optimal_route表示當前最優路徑,current_optimal_distance表示當前最優路徑的長度
        current_optimal_route = rd.permutation(np.arange(self.N))
        current_optimal_distance = self.calc_route_distance(current_optimal_route)
        self.current_optimal_distance_of_every_iteration.append(current_optimal_distance)
        # 控制迴圈代數
        for times in range(1, 1 + self.run_times):
            if times % int(0.1 * self.run_times) == 0:
                print("第%d次迭代:" % times)
                print("最優路徑:", current_optimal_route)
                print("最優路徑長度:", current_optimal_distance)
            # 每隻螞蟻已經經過的城市列表[lst1, lst2, lst3, ...],lst1表示第一隻螞蟻經過城市列表,lst1[-1]表示螞蟻1當前所在城市,以此類推
            passed_city = [[rd.randint(0, self.N)] for i in range(self.ant_count)]
            # 每隻螞蟻未經過的城市列表[lst1, lst2, ...],lst1表示第一隻螞蟻未經過的城市列表,以此類推
            not_passed_city = [list(set(range(self.N)) - set(i)) for i in passed_city]
            # 判斷每隻螞蟻是否均遍歷完所有城市,如果遍歷完則跳出迴圈
            while len(np.unique(not_passed_city)) != 0:
                # 每次迴圈所有螞蟻選擇一個城市進行狀態轉移
                # 計算未經過城市中每個城市被選擇的概率矩陣move_prob_matrix,形狀為[self.ant_count, np.array(not_passed_city).shape[1]]
                move_prob_matrix = self.calc_move_prob_matrix(passed_city, not_passed_city)
                # 求取被選擇概率最大的城市索引
                select_city_index = np.argmax(move_prob_matrix, axis=1)
                # 進行狀態轉移,更新已經經過的城市列表以及未經過的城市列表
                for i in range(self.ant_count):
                    passed_city[i].append(not_passed_city[i].pop(select_city_index[i]))
            # 更新路徑等級,首先進行懲罰
            self.route_level_matrix -= self.r_1
            self.route_level_matrix[self.route_level_matrix < 1] = 1
            # 更新當前最優路徑以及當前最優路徑長度
            # 計算所有螞蟻經過的路徑的路徑長度
            distance_lst = [self.calc_route_distance(r) for r in passed_city]
            # 最優螞蟻索引
            optimal_index = np.argmin(distance_lst)
            current_iter_optimal_route = passed_city[optimal_index]
            current_iter_optimal_distance = distance_lst[optimal_index]
            self.optimal_distance_of_every_iteration.append(current_iter_optimal_distance)
            if current_iter_optimal_distance < current_optimal_distance:
                current_optimal_distance = current_iter_optimal_distance
                current_optimal_route = current_iter_optimal_route
            self.current_optimal_distance_of_every_iteration.append(current_optimal_distance)
            # 更新路徑等級,進行獎勵
            for i in range(len(current_optimal_route) - 1):
                min_value, max_value = sorted([current_optimal_route[i], current_optimal_route[i + 1]])
                self.route_level_matrix[min_value, max_value] += self.r_2
            min_value, max_value = sorted([current_optimal_route[0], current_optimal_route[-1]])
            self.route_level_matrix[min_value, max_value] += self.r_2
            self.route_level_matrix[self.route_level_matrix > self.M] = self.M
            # 更新資訊素
            self.g_function()
#             print("最優路徑:", current_optimal_route)
#             print("最優路徑長度:", current_optimal_distance)
        return current_optimal_route, current_optimal_distance


# In[6]:


fgp_aco = FGP_ACO(100, 3, 1, 50, 5, 50, 2000, data, 25, 100)
print("城市座標:\n", fgp_aco.city_position_matrix)
print("城市間距離矩陣:\n", fgp_aco.distance_matrix)
print("資訊素矩陣:\n", fgp_aco.pheromone_matrix)
print("啟發資訊矩陣:\n", fgp_aco.heuristic_information_matrix)
fgp_aco.run()


# In[16]:


ax = plt.subplot(1, 1, 1)
ax.scatter(np.arange(len(fgp_aco.optimal_distance_of_every_iteration)), fgp_aco.optimal_distance_of_every_iteration, alpha=0.3)
plt.show()