並行プログラミングのフェイザー多段階タスク(例として遺伝的アルゴリズムTSP問題を取り上げる)

目次

1つは、Phaserクラスを知っている

1.フェイザーの機能

2.タスクの登録とキャンセル

3.同期フェーズの変更

2、遺伝的アルゴリズム

1. DataLoaderクラス:データをロードします

2.個々のカテゴリ:問題に対するすべての可能な解決策

3. GeneticOperatorsクラス:GAアルゴリズムコア

3. GAアプリケーション:Phaserクラスは巡回セールスマン問題(TSP)を解決します

2. GeneticPhaserクラス:Java並行Phaserクラスの実装

3. ConcurrentGeneticTaskクラス:GAアルゴリズムステージタスクの実行

 4. ConcurrentGeneticAlgorithmクラス:GAアルゴリズムの同時実装

5.メインクラスメイン

4.実験結果

5、分析と要約


1つは、Phaserクラスを知っている

私はしばらくの間Javaの並行性を学んでおり、基本から多くの同期メカニズムAPIを学びました。今回は、Phaserクラスの同期メカニズムを学び、以前の理解を更新し、新しい知識を追加しました。

まず、Phaserとは何かを理解し、次に遺伝的アルゴリズムの巡回セールスマン問題(TSP)のケースの古典的なアプリケーションを通じて、実際のアプリケーションと組み合わせて、Phaser同期メカニズムの使用法をよりよく学びましょう。

1.フェイザーの機能

Java7の並行APIはフェイザークラスを導入し、強力な同期機構-提供セグメンタ実行するための複数の段階に、分割タスクを、処理プロセスは、明確な手順を定義し、各ステージタスクニーズの完了の順序に従っします。

主な特徴:

  1. セグメンターは、制御するタスクの数を知っている必要があり、Javaの参加者(タスク)の登録メカニズムになります。
  2. タスクがステージを完了すると、セグメンターに通知されます。このフェーズを完了する前に、セグメンターはタスクを休止状態にします。
  3. セグメンターは、行われたフェーズ変更の数を格納する整数値を保持します。
  4. セグメンターがステージを変更すると、カスタマイズされたコードを実行できます。
  5. フェーザーの終了を制御します。
  6. 参加者の数とステータスは、メソッドを介して取得できます。

2.タスクの登録とキャンセル

上記の最初の機能に基づいて、参加者を登録する方法は次のとおりです。登録は、タスク実行の開始時またはいつでも行うことができます。

Phaserオブジェクトの作成、2つの一般的に使用されるコンストラクター:

  1. Phaser():参加者が0のセグメンターを作成します。
  2. Phaser(int party):指定された数の参加者でセグメンターを作成します。

明示的な作成方法:

  1. BulkRegisterl(int party):指定された数の参加者を登録します。
  2. register():参加者を登録します。

セグメンターによって制御されるタスクが完了したら、ログアウトする必要があります。そうしないと、セグメンターは待機状態になります。

ログアウト方法:

 ArrivateAndDeRegister():タスクが現在のステージを完了し、次のステージに参加しないことをセグメンターに通知します。

3.同期フェーズの変更

セグメンターの主な目的は、複数のステージに分割して同時に実行できるアルゴリズムを作成することです。タスクは、現在のステージを完了した後にのみ次のステージに入ることができます。

Phaserクラスには、arrive、arriveAndDeRegister、arriveAndwaitAdvanceの3つのメソッドがあり、現在のステージが完了したことを通知しますが、タスクの完了後にいずれかのメソッドが呼び出されない場合、セグメンターは残りのタスクまたはステージをブロックする場合があります。

したがって、次のステージに入り、次のメソッドを呼び出します

  1. ArrivateAndwaitAdvance():現在のステージが完了したことをセグメンターに通知し、次のステージに入ります。フェーザーは、参加しているすべてのタスクが同期メソッドの1つを呼び出すまで、タスクをブロックします。
  2. awaitAdvance(int phaser):パラメーターが実際のフェーズ数と等しい場合は現在のフェーズの終了を待機し、そうでない場合は戻ることをセグメンターに通知します。

2、遺伝的アルゴリズム

上記は実際には理論的な知識ですが、Phaserをある程度理解した後、実際の事例から学び始め、さらに理解を深めました。遺伝的アルゴリズムが良い例です。

遺伝的アルゴリズムはよく知られており、自然淘汰の原理に基づく適応ヒューリスティック探索アルゴリズムであり、問​​題を最適化し、探索問題のより良い解決策を見つけるために使用されます言い換えれば、「適者生存、適者生存」です。これも生物学から派生した同様の原理を持つアルゴリズムであるため、いくつかの生物学的用語も使用されます。

遺伝的アルゴリズムで使用されるいくつかの特定の用語:

  1. 適応度関数:これは主な目標であり、関数を最大化または最小化してソリューションの長所と短所を決定できるソリューションです。
  2. 母集団:問題に対するすべての可能な解決策のコレクション。初期セットはランダムに生成することも、ヒューリスティック関数を使用して初期解を取得することもできます。

最初の母集団の後、次の3つの段階で構成される反復プロセスを開始します。

  1. 選択:母集団の中からより良い個人を選択すると、その個人は適応度関数においてより良い価値を持ちます。
  2. クロスオーバー:前のステップで高品質の個人を選択した後にクロスオーバーして、新世代の個人を生成します。特定の操作:parent1 X parent2->> child1 + child2、主に解決する問題に依存します。
  3. 突然変異:突然変異演算子は、個人の値を変更するために適用できます。この操作は、ごく少数の個人に対してのみ実行されます。

終了基準:

  • 固定代数
  • 適応度関数によって設定されたしきい値
  • 所定の基準を満たす計画を見つける
  • 制限時間
  • 手動停止

遺伝的アルゴリズムをTSP問題に適用すると、目標は、各都市を1回だけ通過する最適なルートを見つけることであり、同時に総移動距離が最短になります。

15都市と57都市を含む2つのデータのコピーがあります。2次元マトリックスの各値は、都市間の距離を表します。

1. DataLoaderクラス:データをロードします

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;

public class DataLoader {

    /**
     * @param path
     * @return int[][]
     * @author Charzous
     * @date 2021/2/5 10:46
     *
     * 加载文件,转化为二维数组
     */
    public static int[][] load(Path path) throws IOException {
        InputStream in = Files.newInputStream(path);
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        String line = null;
        String[] row = null;
        int[][] a;
        ArrayList<String> arrayList = new ArrayList<>();
        int rowNum = 0, colNum = 0, i = 0, j = 0;
        while ((line = reader.readLine()) != null) {
            arrayList.add(line);
            colNum = line.split(",").length;
        }
        rowNum = arrayList.size();
        a = new int[rowNum][colNum];
        int count = 0;

        for (String str : arrayList) {
            row = str.split(",");
            for (i = 0; i < colNum; i++) {
                a[count][i] = Integer.parseInt(row[i]);
            }
            count++;
        }
        return a;
    }
/**
 * @param args
 * @return void
 * @author Charzous
 * @date 2021/2/5 10:45
 *
 * 类内测试,加载文件
 */
//    public static void main(String[] args) throws IOException {
//        Path file = Paths.get("data/kn57_dist.txt");
//        int[][] a;
//        a = DataLoader.load(file);
//        for (int i = 0; i < a.length; i++) {
//            for (int j = 0; j < a[0].length; j++) {
//                System.out.print(a[i][j] + " ");
//            }
//            System.out.println();
//        }
//    }
}

2.個々のカテゴリ:問題に対するすべての可能な解決策

/**
 * 存放访问不同城市的顺序,TSP问题的所有可能解
 *
 * @author Charzous
 * @date 2021/2/5 10:53
 */
public class Individual implements Comparable<Individual> {

    private Integer[] chromosomes;
    private int value;

    public Integer[] getChromosomes() {
        return chromosomes;
    }

    public void setChromosomes(Integer[] chromosomes) {
        this.chromosomes = chromosomes;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public Individual(int size) {
        chromosomes = new Integer[size];
    }

    public Individual(Individual other) {
        chromosomes = other.getChromosomes().clone();
    }

    @Override
    public int compareTo(Individual o) {
        return Integer.compare(this.getValue(), o.getValue());
    }

    @Override
    public String toString() {
        String ret = "";
        for (Integer number : chromosomes)
            ret += number + ",";
        return ret;
    }
}

3. GeneticOperatorsクラス:GAアルゴリズムコア

遺伝的アルゴリズムのコア内部ロジックの実装:選択、クロスオーバー、個人および集団の評価、詳細についてはコードコメントを参照してください


import java.util.*;
/**
 * 实现遗传算法内部逻辑
 *
 * @author Charzous
 * @date 2021/2/5 11:21
 *
 */
public class GeneticOperators {
    /**
     * @param selected
     * @param numberOfIndividual
     * @param size
     * @return GeneticAlgorithm.Individual[]
     * @author Charzous
     * @date 2021/2/5 11:29
     *
     * 接收被选中的个体,交叉操作生成下一代种群
     */
    public static Individual[] crossover(Individual[] selected,int numberOfIndividual,int size){

        Individual population[] =new Individual[numberOfIndividual];
        Random random=new Random(System.nanoTime());
        for (int i=0;i<numberOfIndividual/2;i++){
            population[2*i]=new Individual(size);
            population[2*i+1]=new Individual(size);

            int p1Idx=random.nextInt(selected.length);
            int p2Idx;
            do {
                p2Idx=random.nextInt(selected.length);
            }while (p1Idx==p2Idx);

            Individual parent1=selected[p1Idx];
            Individual parent2=selected[p2Idx];

            crossover(parent1,parent2,population[2*i],population[2*i+1]);
        }
        return population;
    }

    /**
     * @param parent1
     * @param parent2
     * @param individual1
     * @param individual2
     * @return void
     * @author Charzous
     * @date 2021/2/5 11:31
     *
     *  parent1 X parent2 --> individual1 , individual2
     */
    public static void crossover(final Individual parent1,final Individual parent2,final Individual individual1,final Individual individual2){
        List<Integer> p1= Arrays.asList(parent1.getChromosomes());
        List<Integer> p2= Arrays.asList(parent2.getChromosomes());
        List<Integer> ch1=new ArrayList<Integer>(p1.size());
        List<Integer> ch2=new ArrayList<Integer>(p2.size());//p1 ? p2
        int size=p1.size();
        Random random=new Random();
        int number1=random.nextInt(size-1);
        int number2;
        //1,14,6,2,3,5,7,0,12,4,11,13,9,8,10,
        do {
            number2=random.nextInt(size);
        }while (number1==number2);

        int start=Math.min(number1,number2);
        int end=Math.max(number1,number2);
        ch1.addAll(p1.subList(start,end));
        ch2.addAll(p2.subList(start,end));

        int currentCity=0;
        int currentCityParent1=0;
        int currentCityParent2=0;
        for (int i = 0; i < size; i++) {
            currentCity=(end+i)%size;
            currentCityParent1=p1.get(currentCity);
            currentCityParent2=p2.get(currentCity);

            if (!ch1.contains(currentCityParent2))
                ch1.add(currentCityParent2);
            if (!ch2.contains(currentCityParent1))
                ch2.add(currentCityParent1);
        }

        Collections.rotate(ch1,start);
        Collections.rotate(ch2,start);
        individual1.setChromosomes(ch1.toArray(individual1.getChromosomes()));
        individual2.setChromosomes(ch2.toArray(individual2.getChromosomes()));
    }
    /**
     * @param population
     * @return GeneticAlgorithm.Individual[]
     * @author Charzous
     * @date 2021/2/5 11:23
     *
     * 获取种群的最优个体,返回种群一半的个体,可使用最适合函数选择
     */
    public static Individual[] selection(Individual[] population){
        Individual selected[] =new Individual[population.length/2];

        for (int i = 0; i < selected.length; i++) {
            selected[i]=new Individual(population[i]);
        }
        return selected;
    }

    /**
     * @param numberOfIndividual
     * @param size
     * @return GeneticAlgorithm.Individual[]
     * @author Charzous
     * @date 2021/2/5 11:24
     *
     * 创建一个群,城市数目size
     */
    public static Individual[] initialize(int numberOfIndividual,int size){
        Individual population[] =new Individual[numberOfIndividual];
        for (int i = 0; i < numberOfIndividual; i++) {
            population[i]=new Individual(size);
            initialize(population[i].getChromosomes());
        }
        return population;
    }

    /**
     * @param chromosomes
     * @return void
     * @author Charzous
     * @date 2021/2/5 11:25
     *
     * 随机初始化某一个体的染色体,生成合法的个体(即每个城市只访问一次)
     */
    public static void initialize(Integer[] chromosomes){
        int size=chromosomes.length;
        List<Integer> values=new ArrayList<Integer>(size);

        for (int i = 0; i < size; i++) {
            values.add(i);
        }
        Collections.shuffle(values,new Random(System.nanoTime()));

        for (int i = 0; i < size; i++) {
            chromosomes[i]=values.get(i);
        }
    }
    /**
     * @param population
     * @param distanceMatrix
     * @return void
     * @author Charzous
     * @date 2021/2/5 11:33
     *
     * 接收距离矩阵,将适应函数应用到全部个体,对种群排序
     */
    public static void evaluate(Individual[] population,int[][] distanceMatrix){
        for (Individual individual:population) {
            evaluate(individual,distanceMatrix);
        }
        Arrays.sort(population);
    }
    /**
     * @param individual
     * @param distanceMatrix
     * @return void
     * @author Charzous
     * @date 2021/2/5 11:36
     *
     * 接将适应函数应用到一个个体,结果加入到个体集合,以供对种群所有进行排序
     */
    public static void evaluate(Individual individual,int[][] distanceMatrix){
        Integer chromosomes[]=individual.getChromosomes();
        int totalDistance=0;
        int source,destination;
        for (int i = 0; i < chromosomes.length-1; i++) {
            source=chromosomes[i];
            destination=chromosomes[i+1];
            totalDistance+=distanceMatrix[source][destination];
        }
        source=chromosomes[chromosomes.length-1];
        destination=chromosomes[0];
        totalDistance+=distanceMatrix[source][destination];

        individual.setValue(totalDistance);
    }
}

3. GAアプリケーション:Phaserクラスは巡回セールスマン問題(TSP)を解決します

1. SharedDataクラス:共有オブジェクト

import java.util.concurrent.atomic.AtomicInteger;
/**
 * 存放任务之间共享的对象
 *
 * @author Charzous
 * @date 2021/2/5 15:16
 *
 */
public class SharedData {
    private Individual[] population;
    private Individual[] selected;
    private AtomicInteger index;
    private Individual best;
    private int[][] distanceMatrix;

    public SharedData() {
        index=new AtomicInteger();
    }

    public Individual[] getPopulation() {
        return population;
    }

    public void setPopulation(Individual[] population) {
        this.population = population;
    }

    public Individual[] getSelected() {
        return selected;
    }

    public void setSelected(Individual[] selected) {
        this.selected = selected;
    }

    public AtomicInteger getIndex() {
        return index;
    }

    public void setIndex(AtomicInteger index) {
        this.index = index;
    }

    public Individual getBest() {
        return best;
    }

    public void setBest(Individual best) {
        this.best = best;
    }

    public int[][] getDistanceMatrix() {
        return distanceMatrix;
    }

    public void setDistanceMatrix(int[][] distanceMatrix) {
        this.distanceMatrix = distanceMatrix;
    }
}

2. GeneticPhaserクラス:Java並行Phaserクラスの実装

import java.util.Arrays;
import java.util.concurrent.Phaser;

public class GeneticPhaser extends Phaser {
    private SharedData data;

    public GeneticPhaser(int parties, SharedData data) {
        super(parties);
        this.data=data;
    }
    /**
     * @param phase
     * @param registeredParties
     * @return boolean
     * @author Charzous
     * @date 2021/2/5 15:20
     *
     * 重载onAdvance方法,使所有任务完成第一阶段之后执行代码
     */
    @Override
    protected boolean onAdvance(int phase, int registeredParties) {
        int realPhase=phase%3;
        if (registeredParties>0) {
            switch (realPhase) {
                case 0:
                case 1:
                    data.getIndex().set(0);
                    break;
                case 2:
                    Arrays.sort(data.getPopulation());
                    if  (data.getPopulation()[0].getValue() < data.getBest().getValue()) {
                        data.setBest(data.getPopulation()[0]);
                    }
                    break;
            }
            return false;
        }
        return true;
    }

    public SharedData getData() {
        return data;
    }

    public void setData(SharedData data) {
        this.data = data;
    }
}

3. ConcurrentGeneticTaskクラス:GAアルゴリズムステージタスクの実行

import JavaConcurrencyProgramming.chapter06.GeneticAlgorithm.GeneticOperators;
import JavaConcurrencyProgramming.chapter06.GeneticAlgorithm.Individual;

import java.util.Random;
/**
 * 执行遗传算法各个阶段的任务
 *
 * @author Charzous
 * @date 2021/2/5 15:28
 *
 */
public class ConcurrentGeneticTask implements Runnable {
    private GeneticPhaser phaser;
    private SharedData data;
    private int numberOfGenerations;
    private boolean main;

    public ConcurrentGeneticTask(GeneticPhaser phaser, int numberOfGenerations,
                                 boolean main) {
        this.phaser = phaser;
        this.numberOfGenerations = numberOfGenerations;
        this.main = main;
        this.data = phaser.getData();
    }

    @Override
    public void run() {
        Random random = new Random((System.nanoTime()));
        for (int i = 0; i < numberOfGenerations; i++) {
            if (main)
                data.setSelected(GeneticOperators.selection(data.getPopulation()));

            phaser.arriveAndAwaitAdvance();

            //交叉操作
            int individualIndex;
            do {
                individualIndex = data.getIndex().getAndAdd(2);
                if (individualIndex < data.getPopulation().length) {
                    int secondIndividual = individualIndex++;

                    int p1Index = random.nextInt(data.getSelected().length);
                    int p2Index;
                    do {
                        p2Index = random.nextInt(data.getSelected().length);
                    } while (p1Index == p2Index);

                    Individual parent1 = data.getSelected()[p1Index];
                    Individual parent2 = data.getSelected()[p2Index];
                    Individual individual1 = data.getPopulation()[individualIndex];
                    Individual individual2 = data.getPopulation()[secondIndividual];

                    GeneticOperators.crossover(parent1, parent2, individual1, individual2);
                }
            } while (individualIndex < data.getPopulation().length);
            phaser.arriveAndAwaitAdvance();

            do {
                individualIndex = data.getIndex().getAndIncrement();
                if (individualIndex < data.getPopulation().length) {
                    GeneticOperators.evaluate(data.getPopulation()[individualIndex], data.getDistanceMatrix());
                }
            } while (individualIndex < data.getPopulation().length);
            phaser.arriveAndAwaitAdvance();
        }
        phaser.arriveAndDeregister();
    }
}

 4. ConcurrentGeneticAlgorithmクラス:GAアルゴリズムの同時実装

/**
 * 遗传算法并发算法
 * @author Charzous
 * @date 2021/2/5 15:30
 *
 */
public class ConcurrentGeneticAlgorithm {
    private int numberOfGenerations;
    private int numberOfIndividuals;
    private int[][] distanceMatrix;
    private int size;

    public ConcurrentGeneticAlgorithm(int[][] distanceMatrix, int numberOfGenerations, int numberOfIndividuals) {
        this.distanceMatrix = distanceMatrix;
        this.numberOfGenerations = numberOfGenerations;
        this.numberOfIndividuals = numberOfIndividuals;
        size = distanceMatrix.length;
    }

    public Individual calculate() {

        Individual[] population = GeneticOperators.initialize(numberOfIndividuals, size);
        GeneticOperators.evaluate(population, distanceMatrix);

        SharedData data = new SharedData();
        data.setPopulation(population);
        data.setDistanceMatrix(distanceMatrix);
        data.setBest(population[0]);

        int numTasks = Runtime.getRuntime().availableProcessors();
        GeneticPhaser phaser = new GeneticPhaser(numTasks, data);

        ConcurrentGeneticTask[] tasks = new ConcurrentGeneticTask[numTasks];
        Thread[] threads = new Thread[numTasks];

        tasks[0] = new ConcurrentGeneticTask(phaser, numberOfGenerations, true);
        for (int i = 1; i < numTasks; i++) {
            tasks[i] = new ConcurrentGeneticTask(phaser, numberOfGenerations, false);
        }

        for (int i = 0; i < numTasks; i++) {
            threads[i] = new Thread(tasks[i]);
            threads[i].start();
        }

        for (int i = 0; i < numTasks; i++) {
            try {
                threads[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        return data.getBest();
    }
}

5.メインクラスメイン

import java.io.IOException;
import java.nio.file.Paths;

public class ConcurrentMain {
    public static void main(String[] args) throws IOException {

        long start, end;

        int generations = 1000;
        int individuals = 1000;

        String[] names={"kn57_dist"};//"lau15_dist" ,
        for (String name:names) {
            int[][] distanceMatrix = DataLoader.load(Paths.get("data", name + ".txt"));

            ConcurrentGeneticAlgorithm concurrentGeneticAlgorithm = new ConcurrentGeneticAlgorithm(distanceMatrix, generations,
                    individuals);
            start = System.currentTimeMillis();
            Individual result = concurrentGeneticAlgorithm.calculate();
            end = System.currentTimeMillis();
            System.out.println("=======================================");
            System.out.println("Example:"+name);
            System.out.println("Generations: " + generations);
            System.out.println("Population: " + individuals);
            System.out.println("Execution Time: " + (end - start)+"ms");
            System.out.println("Best Individual: " + result);
            System.out.println("Total Distance: " + result.getValue());
            System.out.println("=======================================");
        }

    }
}

 

4.実験結果

データと実験パラメータの設定:

世代= 1000;
個人= 1000;

データ:lau15_dist.txt(15都市)

テストは10回実行され、平均実行時間は約500msです。

 

もちろん、母集団数と代数は他の値に設定でき、データセットは実験のために変更できます。

並行バージョンのパフォーマンス向上を比較するために、シングルスレッドバージョンを単純に実装します。ConcurrentGeneticAlgorithmクラスの並行部分をシングルスレッドメソッドに変更するだけで済みます。結果は次のとおりです。

テストは10回実行され、平均実行時間は約690ミリ秒でした。比較すると、同時実行性のパフォーマンスの向上が見られます。

五数要約

この記事では、Phaserクラスの同期メカニズムの原理と実践的な理解を記録します。私はJavaの同時実行性を一定期間学びました。基本から始めて、多くの同期メカニズムAPIを学びました。今回はPhaserクラスの同期メカニズムを学びました。以前の理解を更新し、新しい知識を追加します。最初にPhaserとは何かを理解し、次に遺伝的アルゴリズムの巡回セールスマン問題(TSP)のケースの古典的なアプリケーションを通じて実際のアプリケーションを組み合わせて、Phaser同期メカニズムの使用方法をよりよく学習します。Phaserの理論的知識をある程度理解した後、実際の事例から学び始め、遺伝的アルゴリズムが良い例であることをさらに理解しました。並行プログラミングでのセグメンテーションの基本的な使用法を習得しました。後でAppsができることを学びたいと思います。助けて。

良いと思う場合は、ブックマーク、フォロー、質問がある場合は直接コメント、交換して学ぶなど、「ワンクリック、3リンク」へようこそ。

Java並行プログラミングシリーズの記事:

  1. オリジナルのJava並行APIケース分析の並行性設計原則[ https://blog.csdn.net/Charzous/article/details/112603639 ]
  2. ハードディスクファイル検索を実現するためのオリジナルのJava同時実行性(Runnable + Thread) [ https://blog.csdn.net/Charzous/article/details/112853937 ]
  3. 並行プログラミングの元の呼び出し可能/将来のインターフェース(例として単語のベストマッチングアルゴリズムを取り上げます [ https://blog.csdn.net/Charzous/article/details/113338669 ]
  4. 並行プログラミングのフェイザー多段階タスク(例として遺伝的アルゴリズムのTSP問題を取り上げます [ https://blog.csdn.net/Charzous/article/details/113698041 ]

私のCSDNブログ:https://blog.csdn.net/Charzous/article/details/113698041

おすすめ

転載: blog.csdn.net/Charzous/article/details/113698041