进化算法多种群并行优化

一、引言

之前的文章均使用的是标准遗传算法(单种群),其并非是完美无缺的算法。随着遗传算法的广泛应用以及研究的深入,标准遗传算法出现严重的早熟收敛问题。其主要表现在群体中的所有个体都趋于同一状态而停止进化。早熟收敛与选择操作、交叉操作、变异操作、群体规模和迭代次数等均有关系。

针对遗传算法存在的不足,出现了一种多种群遗传算法(MPGA)来取代常规的标准遗传算法(SGA)。MPGA在SGA的基础上有以下改进:

  • 引入多种群同时进行优化搜索,并且不同种群赋以不同的控制参数。
  • 各个种群之间通过移民算子进行联系(多个种群协同进化)。
  • 通过人工选择算子保存各种群每个进化代中的最优个体形成精华种群,并作为判断算法收敛的依据。

二、进化算法多种群并行计算

进化算法是内在并行的,即进化算法本身非常适合大规模并行,如多种遗传算法(Multi-Population Genetic Algorithm,MPGA),多种群文化基因算法(Multi-Population Memetic Algorithm,MPMA)设置多个种群,通过利用多种群实现多种优化策略,并采用并行方式,使得算法可以采用不同的优化策略进行搜索,更加节省计算时间

Java使用Thread和Runnable开启多线程

import geneticalgorithm.util.Util;

public class GeneticAlgorithm {

    public static void main(String[] args) throws InterruptedException {
        GeneticAlgorithm ga = new GeneticAlgorithm();


        int processors = Runtime.getRuntime().availableProcessors();
        System.out.println("CPU cores: " + processors);

        long avgtime = 0;
        for (int i = 0; i < 40; i++) {
            avgtime += ga.parallel();
        }
        avgtime /= 40;

        System.out.println(avgtime);


        avgtime = 0;
        for (int i = 0; i < 40; i++) {
            avgtime += ga.serial();
        }
        System.out.println(avgtime);
    }


    public int[][] pop1;
    public int[][] pop2;
    public int lind = 10000;
    public int nind = 100;

    public GeneticAlgorithm() {
        this.pop1 = new int[nind][lind];
        this.pop2 = new int[nind][lind];
        this.initPops();
    }

    public void initPop(int[][] pop) {
        for (int i = 0; i < pop.length; i++) {
            pop[i] = Util.randArr(lind);
//            System.out.println(Arrays.toString(pop[i]));
        }
    }

    public void initPops() {
        initPop(pop1);
        initPop(pop2);
    }

    public void mutate(int[][] pop) {
        //i从1开始 保留精英
        for (int i = 1; i < this.nind - 1; i += 2) {
            int r1 = Util.randInt(this.lind);//产生[0,20)的随机整数
            int r2 = Util.randInt(this.lind);
            int gene1 = pop[i][r1];
            int gene2 = pop[i + 1][r2];
            pop[i][r1] = gene2;
            pop[i][r2] = gene1;
        }
    }
    
    public long parallel() throws InterruptedException {
        Thread t1 = new Thread(() -> mutate(pop1), "t1");
        Thread t2 = new Thread(() -> mutate(pop2), "t2");
        t1.start();
        t2.start();
        long begin = System.nanoTime();
        t1.join(); // 确保t1执行完毕
        t2.join(); // 确保t2执行完毕
        long end = System.nanoTime();
        return end - begin;
    }

    public long serial() {
        long begin = System.nanoTime();
        mutate(pop1);
        mutate(pop2);
        long end = System.nanoTime();
        return end - begin;
    }
}

Python

from threading import Thread
import numpy as np


class MyThread(Thread):
    def __init__(self, func, args):
        '''
        :param func: 可调用的对象
        :param args: 可调用对象的参数
        '''
        Thread.__init__(self)
        self.func = func
        self.args = args
        self.result = None

    def run(self):
        self.result = self.func(*self.args)

    def getResult(self):
        return self.result


def twoPointSwap(population):
    for chrom in population:
        r1 = np.random.randint(0, len(chrom))
        r2 = np.random.randint(0, len(chrom))
        chrom[r1], chrom[r2] = chrom[r2], chrom[r1]
    return population


NIND = 10
LIND = 10
population = np.zeros(shape=(NIND, LIND), dtype=np.int64)
for i in range(NIND):
    population[i] = np.arange(0, LIND)
    np.random.shuffle(population[i])

# 创建 Thread 实例
episode = 5
t = [MyThread(None, args=(None,))] * episode
for j in range(episode):
    t[j] = MyThread(func=twoPointSwap, args=(population,))

# 启动线程运行
[t[j].start() for j in range(episode)]

# 等待所有线程执行完毕
[t[j].join() for j in range(episode)]  # join() 等待线程终止,要不然一直挂起

print("*" * 100)
for i in range(episode):
    print(t[i].getResult())
    print()


Python开启多线程的两种方式

方法1

import time
from threading import Thread


def func(name):
    print(f"{name}开始")
    time.sleep(0.5)
    print(f"{name}结束")


if __name__ == '__main__':
    t1 = Thread(target=func, args=("线程1",))
    t2 = Thread(target=func, args=("线程2",))
    t1.start()
    t2.start()
    print("主线程结束")

方法2

import time
from threading import Thread


class MyThread(Thread):
    def __init__(self, name):  # 可以通过初始化来传递参数
        super(MyThread, self).__init__()
        self.name = name

    def run(self):  # 必须有的函数
        print(f"{self.name}开始")
        time.sleep(0.2)
        print(f"{self.name}结束")


if __name__ == '__main__':
    t1 = MyThread("线程1")  # 创建第一个线程,并传递参数
    t2 = MyThread("线程2")  # 创建第二个线程,并传递参数
    t1.start()  # 开启第一个线程
    t2.start()  # 开启第二个线程
    print("主线程执行结束,子线程是依附于主线程存在的,所以,子线程都结束后,主线程才真正的结束。")

参考:

  • Gutierrez A, Dieulle L, Labadie N, et al. A multi-population algorithm to solve the VRP with stochastic service and travel times[J/OL]. Computers & Industrial Engineering, 2018, 125: 144-156. https://doi.org/10.1016/j.cie.2018.07.042.
  • 张健欣, 童朝南. 通过多种群协进化Memetic算法求解TSP[J]. 信息与控制, 2009, 38(3): 376-380.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值