• 蚁群算法解决任务调度问题-Python


      蚁群算法是一种启发式优化算法,也是一种智能算法、进化计算。和遗传算法、粒子群算法相比,蚁群算法所优化的内容是拓扑序(或者路径)的信息素浓度,而遗传算法、粒子群算法优化的是某一个个体(解向量)。

      例如TSP问题,30个城市之间有900个对应关系,30*15/2=435条路径,在蚂蚁经过之后,都留下了信息素,而某一个拓扑序指的是一个解向量,旅行商的回路应是首尾相连的30条路径,蚂蚁在走路的时候会考虑到能走的路径上的信息素浓度,然后选择一个拓扑序作为新解。例如任务车间调度问题,假如有8个机器和10个任务,则一共有80个对应关系,每个对应关系看作一个路径,会有信息素的标记,而解向量是一个长度为10的向量,蚂蚁在走路的时候,会选择每一个任务到所有机器的对应关系,考虑信息素的浓度选择其中一个,由此选择10个任务各自放在哪一个机器上,作为新解。

      蚁群算法天生适合解决路径问题、指派问题,效果通常比粒子群等要好。相比于模拟退火算法,计算更稳定。相比于粒子群算法收敛性更好。

    对于任务调度问题,需要定义模型对象:

      节点和云任务。节点VM具有节点编号id、两种资源总量和两种资源的已占有量。因此其剩余空间是总量capacity减去已占有量supply。

    class Cloudlet:
        def __init__(self, cpu_demand: float, mem_demand: float):
            self.cpu_demand = cpu_demand
            self.mem_demand = mem_demand
    
    
    class VM:
        def __init__(self, vm_id: int, cpu_supply: float, cpu_velocity: float, mem_supply: float, mem_capacity: float):
            self.id = vm_id
            self.cpu_supply = cpu_supply
            self.cpu_velocity = cpu_velocity
            self.mem_supply = mem_supply
            self.mem_capacity = mem_capacity

    然后定义蚁群算法解决任务调度问题的类。

      初始化属性有云任务列表,节点列表,蚂蚁种群数量,迭代次数,以及人物的路径数和最优策略记录。

      函数有生成新解的函数gen_topo_jobs(),评估解的负载均衡值和计算适应度evaluate_particle、calculate_fitness,更新信息素update_topo以及蚁群算法主流程schedulet_main。后面逐一讲解

    import numpy as np
    from typing import List
    from matplotlib import pyplot as plt
    
    
    class ACScheduler:
        def __init__(self, cloudlets, vms, population_number=100, times=500):
            self.cloudlets = cloudlets
            self.vms = vms
            self.cloudlet_num = len(cloudlets)  # 任务数量也就是粒子长度
            self.machine_number = len(vms)  # 机器数量
    
            self.population_number = population_number  # 种群数量
            self.times = times  # 迭代代数
    
            # 表示任务选择的机器的信息素
            self.topo_phs = [[100 for _ in range(self.machine_number)] for _ in range(self.cloudlet_num)]
            # 最优策略
            self.best_topo = None
    
        # 生成新的解向量--根据信息素浓度生成
        def gen_topo_jobs(self):
            pass
    
        # 与算法无关的函数--评估当前解向量
        def evaluate_particle(self, topo_jobs: List[int]) -> int:
            pass
    
        # 与算法无关的函数--计算适应度
        def calculate_fitness(self, topo_jobs: List[int]) -> float:
            pass
    
        # 更新信息素
        def update_topo(self):
            pass
    
        def scheduler_main(self):
            pass
    
    
    if __name__ == '__main__':
        nodes = [VM(0, 0.862, 950, 950, 1719), VM(1, 0.962, 2, 950, 1719), VM(2, 1.062, 2, 1500, 1719)]
        lets = [Cloudlet(0.15, 50), Cloudlet(0.05, 100), Cloudlet(0.2, 60),
                Cloudlet(0.01, 70), Cloudlet(0.04, 80), Cloudlet(0.07, 20),
                Cloudlet(0.14, 150), Cloudlet(0.15, 200), Cloudlet(0.03, 40), Cloudlet(0.06, 90)]
        ac = ACScheduler(lets, nodes, times=150)
        res = ac.scheduler_main()
        i = 0
        for _ in ac.best_topo:
            print("任务:", i, " 放置到机器", ac.best_topo[i], "上执行")
            i += 1

     1. 生成新的解向量gen_topo_jobs(self)

      初始化解向量为-1,然后逐个根据信息素的浓度选择任务的对应的机器,以此生成新的解向量返回

        # 生成新的解向量--根据信息素浓度生成
        def gen_topo_jobs(self):
            ans = [-1 for _ in range(self.cloudlet_num)]
            node_free = [node_id for node_id in range(self.machine_number)]
            for let in range(self.cloudlet_num):
                ph_sum = np.sum(list(map(lambda j: self.topo_phs[let][j], node_free)))
                test_val = 0
                rand_ph = np.random.uniform(0, ph_sum)
                for node_id in node_free:
                    test_val += self.topo_phs[let][node_id]
                    if rand_ph <= test_val:
                        ans[let] = node_id
                        break
            return ans

    2. 评估解向量然后计算适应度

      把解向量的策略利用到集群中,然后计算每个节点每个资源的利用率,然后对所有节点取标准差,各个资源的利用率标准差加和作为负载均衡度,越小越均衡。

      由于适应度定义为越大越好,因此评估结果取倒数作为适应度。

        # 与算法无关的函数--评估当前解向量
        def evaluate_particle(self, topo_jobs: List[int]) -> int:
            cpu_util = np.zeros(self.machine_number)
            mem_util = np.zeros(self.machine_number)
            for i in range(len(self.vms)):
                cpu_util[i] = self.vms[i].cpu_supply
                mem_util[i] = self.vms[i].mem_supply
    
            for i in range(self.cloudlet_num):
                cpu_util[topo_jobs[i]] += self.cloudlets[i].cpu_demand
                mem_util[topo_jobs[i]] += self.cloudlets[i].mem_demand
    
            for i in range(self.machine_number):
                if cpu_util[i] > self.vms[i].cpu_velocity:
                    return 100
                if mem_util[i] > self.vms[i].mem_capacity:
                    return 100
    
            for i in range(self.machine_number):
                cpu_util[i] /= self.vms[i].cpu_velocity
                mem_util[i] /= self.vms[i].mem_capacity
    
            return np.std(cpu_util, ddof=1) + np.std(mem_util, ddof=1)
    
        # 与算法无关的函数--计算适应度
        def calculate_fitness(self, topo_jobs: List[int]) -> float:
            return 1 / self.evaluate_particle(topo_jobs)

    3.  更新信息素:有蚂蚁经过的路径信息素加倍,否则挥发减半

         # 更新信息素
         def update_topo(self):
            for i in range(self.cloudlet_num):
                for j in range(self.machine_number):
                    if j == self.best_topo[i]:
                        self.topo_phs[i][j] *= 2
                    else:
                        self.topo_phs[i][j] *= 0.5

    4. 主流程:迭代求解

        def scheduler_main(self):
            results = [0 for _ in range(self.times)]
            fitness = 0
    
            for it in range(self.times):
                best_time = 0
    
                for ant_id in range(self.population_number):
                    topo_jobs = self.gen_topo_jobs()
                    fitness = self.calculate_fitness(topo_jobs)
                    if fitness > best_time:
                        self.best_topo = topo_jobs
                        best_time = fitness
                assert self.best_topo is not None
                self.update_topo()
                results[it] = best_time
                if it % 10 == 0:
                    print("ACO iter: ", it, " / ", self.times, ", 适应度: ", fitness)
            plt.plot(range(self.times), results)
            plt.xlabel("迭代次数")
            plt.ylabel("适应度")
            plt.rcParams['font.sans-serif'] = ['KaiTi']  # 指定默认字体
            plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题
            plt.title("蚁群算法求解云任务调度负载均衡问题")
            # plt.savefig('img/ACScheduler-1.1_0.9-popu100-iter200.png', dpi=300,
            #             format='png')  # bbox_inches="tight"解决X轴时间两个字不被保存的问题
            plt.show()
            return results

    测试数据:

    if __name__ == '__main__':
        # 第四组数据data9
    
        nodes = [
            VM(0, 0.662, 2, 620, 2223),
            VM(1, 0.662, 2, 1100, 2223),
            VM(2, 1.662, 2, 720, 2223),
            VM(3, 0.662, 2, 1100, 2223),
            VM(4, 0.662, 2, 620, 2223),
            VM(5, 0.562, 2, 650, 2223),
            VM(6, 0.562, 2, 620, 2223),
            VM(7, 0.462, 2, 440, 2223),  # 8
        ]
        lets = [
            Cloudlet(0.133364, 272.435810),
            Cloudlet(0.226357, 141.126392),
            Cloudlet(0.084122, 7.183883),
            Cloudlet(0.029290, 96.658838),
            Cloudlet(0.027560, 247.821058),
            Cloudlet(0.191912, 80.636804),
            Cloudlet(0.134658, 220.702279),
            Cloudlet(0.133052, 163.046071),
            Cloudlet(0.272010, 253.477271),
            Cloudlet(0.175000, 19.409176),
            Cloudlet(0.166933, 140.880123),
            Cloudlet(0.286495, 71.288800),
            Cloudlet(0.080714, 354.839232),
            Cloudlet(0.209842, 211.351191),
            Cloudlet(0.221753, 249.500490),
            Cloudlet(0.128952, 81.599575),
            Cloudlet(0.168469, 122.216016),
            Cloudlet(0.049628, 135.728968),
            Cloudlet(0.051167, 230.172949),
            Cloudlet(0.158938, 135.356776),
            Cloudlet(0.212047, 202.830773),
            Cloudlet(0.372328, 13.145747),
            Cloudlet(0.092549, 130.122476),
            Cloudlet(0.166031, 97.761267),
            Cloudlet(0.142820, 45.852985),
            Cloudlet(0.016367, 189.495519),
            Cloudlet(0.112156, 173.926518),
            Cloudlet(0.004466, 156.806505),
            Cloudlet(0.222208, 62.619918),
            Cloudlet(0.073526, 232.175486),
            Cloudlet(0.158527, 178.624649),
            Cloudlet(0.103075, 133.896667),
            Cloudlet(0.176026, 156.076929),
            Cloudlet(0.098275, 136.450544),
            Cloudlet(0.192065, 33.923922),
            Cloudlet(0.213519, 134.820860),
    
        ]
    
        ac = ACScheduler(lets, nodes, times=150)
        res = ac.scheduler_main()
        i = 0
        for _ in ac.best_topo:
            print("任务:", i, " 放置到机器", ac.best_topo[i], "上执行")
            i += 1

    求解结果:

    ACO iter: 0 / 150 , 适应度: 0.01
    ACO iter: 10 / 150 , 适应度: 4.0408235554621665
    ACO iter: 20 / 150 , 适应度: 4.754201711827778
    ACO iter: 30 / 150 , 适应度: 4.754201711827778
    ACO iter: 40 / 150 , 适应度: 4.754201711827778
    ACO iter: 50 / 150 , 适应度: 4.754201711827778
    ACO iter: 60 / 150 , 适应度: 4.754201711827778
    ACO iter: 70 / 150 , 适应度: 4.754201711827778
    ACO iter: 80 / 150 , 适应度: 4.754201711827778
    ACO iter: 90 / 150 , 适应度: 4.754201711827778
    ACO iter: 100 / 150 , 适应度: 4.754201711827778
    ACO iter: 110 / 150 , 适应度: 4.754201711827778
    ACO iter: 120 / 150 , 适应度: 4.754201711827778
    ACO iter: 130 / 150 , 适应度: 4.754201711827778
    ACO iter: 140 / 150 , 适应度: 4.754201711827778

  • 相关阅读:
    面向对象与面向过程
    mul 指令
    Debug 的使用
    子域名扫描器
    div 指令
    C Primer Plus学习笔记(十三)- 结构和其他数据形式
    C Primer Plus学习笔记(十一)- 存储类别、链接和内存管理
    BugkuCTF WEB
    BugkuCTF 逆向
    C Primer Plus学习笔记(十)- 字符串和字符串函数
  • 原文地址:https://www.cnblogs.com/zhaoke271828/p/14585443.html
Copyright © 2020-2023  润新知