\\ \eta_{ij} = \frac{1}{d_{ij}} pijk(t)={kunvisit[τik(t)]α[ηik]β[τij(t)]α[ηij]β,0,junvisitkelseηij=dij1

其中:

根据上式,不难发现两个权重因子直接影响着蚂蚁每一步的决策, α \alpha α 过大会导致蚂蚁倾向走之前的路,搜索其它路径的趋势减小,容易陷入局部最优就难以再逃脱;相反, β \beta β 过大就越倾向于贪心算法,只往当前节点的最小值走.
有了以上节点的概率以后,只需要模拟该概率分布情况做一次采样即可,具体可使用轮盘赌法形式来选择,所谓轮盘堵法是,已知各个选择的概率,每个概率代表在单位长度上所占据的区域长度,然后在该单位长度上随机生成一个数,该值落在哪个区间则选择该节点.程序的实现可以使用cumsum:

probabilities = np.zeros(len(unvisit))

for k in range(len(unvisit)):
    probabilities[k] = np.power(pheromonetable[visiting][unvisit[k]], alpha) * \
                        np.power(eta[visiting][unvisit[k]], beta)

# 使用轮盘赌选择下一个节点
# 累计概率
cumsum = (probabilities / sum(probabilities)).cumsum()
cumsum -= np.random.rand()
# 求出随机值处于哪个概率区间
next = unvisit[list(cumsum > 0).index(True)]
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

3.2 信息素更新

每只蚂蚁根据上面的规则选择路径后,即各自得到一条路径,这时候需要更新信息素(相当于总结归纳经验),以此来引导后面迭代蚂蚁的选择.具体的更新如下:

τ i j ( t + 1 ) = ( 1 − p ) τ i j ( t ) + Δ τ i j Δ τ i j = ∑ k = 1 m Δ τ i j k \tau_{ij}(t+1) = (1-p)\tau_{ij}(t) + \Delta \tau_{ij} \\ \Delta \tau_{ij} = \sum_{k=1}^{m} \Delta \tau_{ij}^k τij(t+1)=(1p)τij(t)+ΔτijΔτij=k=1mΔτijk

其中,

每只蚂蚁在每个节点连接处增加的信息有3种方法:

直接引用上面博客中的描述

class="table-box">
信息素增量不同信息素更新时刻不同信息素更新形式不同
蚁周模型信息素增量为 Q / L k Q/L_k Q/Lk,它只与搜索路线有关与具体的路径(i,j)无关在第k只蚂蚁完成一次路径搜索后,对线路上所有路径进行信息素的更新信息素增量与本次搜索的整体线路有关,因此属于全局信息更新
蚁量模型信息素增量为 Q / d i j Q/d_{ij} Q/dij,与路径(i,j)的长度有关在蚁群前进过程中进行,蚂蚁每完成一步移动后更新该路径上的信息素利用蚂蚁所走路径上的信息进行更新,因此属于局部信息更新
蚁密模型信息素增量为固定值Q在蚁群前进过程中进行,蚂蚁每完成一步移动后更新该路径上的信息素利用蚂蚁所走路径上的信息进行更新,因此属于局部信息更新

下面的程序将以 蚁周模型 方式更新信息素

3.3 算法实现

经过上面介绍,利用蚁群算法求解TSP问题伪代码可总结如下

初始化参数:建立距离表;设置权重因子;初始化信息素;初始化启发值;设置挥发系数
迭代开始:
    将m只蚂蚁随机投放到节点中
    为每只蚂蚁生成路径:
        选择下一节点直至全部访问:
            根据当前所在节点和选择下一节点的概率,随机选取下一节点
        计算路径总长度
    更新最优路径
    更新信息素
输出路径
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
def SolveTspByAC(nodes, ants = 100, maxIter = 200):
    """
    @brief:Ant Colony 蚁群算法 解决大规模的TSP问题
    """
    numNode = len(nodes)

    distances = np.zeros((numNode, numNode))
    for i in range(numNode):
        for j in range(numNode):
            if i == j:
                distances[i][j] = 1e10
            else:
                # 根据输入的数据结构计算两个节点的距离
                distances[i][j] = (nodes[i] - nodes[j]).norm()


    # 启发矩阵
    eta = 1.0 / distances

    # 信息素
    # 信息素权重因子
    alpha = 1 
    # 启发函数权重因子
    beta = 2
    rho = 0.1
    # 单只蚂蚁一条路径的信息素总和
    Q = 1
    # 信息素矩阵
    pheromonetable = np.ones((numNode, numNode))

    minLenght = 1e10

    for iter in range(maxIter):

        # 记录每只蚂蚁的路径
        paths = np.zeros((ants, numNode), dtype=np.int32)

        # 为每一只蚂蚁选择初始点
        if ants <= numNode:
            paths[:, 0] = np.random.permutation(range(numNode))[:ants]
        else:
            paths[:numNode, 0] = np.random.permutation(range(numNode))[:]
            paths[numNode:, 0] = np.random.permutation(range(numNode))[: (ants - numNode)]
        
        # 记录每只蚂蚁的路径总长
        lengths = np.zeros(ants)

        for i in range(ants):
            unvisit = list(range(numNode))
            visiting = paths[i][0]
            unvisit.remove(visiting)

            for j in range(1, numNode):
                probabilities = np.zeros(len(unvisit))

                for k in range(len(unvisit)):
                    probabilities[k] = np.power(pheromonetable[visiting][unvisit[k]], alpha) * \
                                       np.power(eta[visiting][unvisit[k]], beta)

                # 使用轮盘赌选择下一个节点
                # 累计概率
                cumsum = (probabilities / sum(probabilities)).cumsum()
                cumsum -= np.random.rand()
                # 求出随机值处于哪个概率区间
                next = unvisit[list(cumsum > 0).index(True)]
                paths[i, j] = next
                unvisit.remove(next)

                lengths[i] += distances[visiting][next]
                visiting = next
            # 最后一个节点和第一个节点的距离也加上 是否必须?
            lengths[i] += distances[visiting][paths[i, 0]]


        # 更新最优路径
        if minLenght > lengths.min():
            minLenght = lengths.min()
            bestPath = paths[lengths.argmin()].copy()
        
        # 更新信息素
        delta = np.zeros((numNode, numNode))
        # for i in range(ants):
        #     for j in range(numNode-1):
        #         delta[paths[i, j]][paths[i, j+1]] += (Q / lengths[i])
        #     # 最后一个节点与第一个节点同样需要更新
        #     delta[paths[i, -1]][paths[i, 0]] += (Q / lengths[i])
        for i in range(ants):
            for j in range(numNode-1):
                delta[paths[i, j]][paths[i, j+1]] += (Q / distances[paths[i, j]][paths[i, j+1]])
            # 最后一个节点与第一个节点同样需要更新
            delta[paths[i, j+1]][paths[i, 0]] += (Q / distances[paths[i, j+1]][paths[i, 0]])

        pheromonetable = (1-rho) * pheromonetable + delta

    return bestPath
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

需要注意的是:上面的实现,根据蚁周模型的更新公式,应该对应的是注释了的部分,但是实测发现收敛速度没有未注释的快,而未注释没有找到相关依据,只是参考了博客中如此实现,没有深入考究,但直观上还是说得通.

data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/qq_20604231/article/details/142323288","extend1":"pc","ab":"new"}">>
注:本文转载自blog.csdn.net的AI脚下的巨人的文章"https://blog.csdn.net/qq_20604231/article/details/142323288"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接

评论记录:

未查询到任何数据!