15

私は、本「ゲーム プログラマーのための AI テクニック」から取り上げたテクニックに基づいて、遺伝的アルゴリズムを書こうとしています。 2 次元配列でプログラム内でランダムに生成されます。

私は最近、疑似コードに出くわし、それを実装しようとしましたが、実行する必要があることの詳細に関していくつかの問題に遭遇しました。私は多くの本といくつかのオープンソース コードをチェックしましたが、まだ進歩するのに苦労しています。人口の合計適合度の合計を取得し、合計とゼロの間の乱数を選択し、その数が親よりも大きい場合はそれを上書きする必要があることを理解していますが、これらのアイデアの実装に苦労しています.

私のJavaは錆びているので、これらのアイデアの実装に助けがあれば大歓迎です。

4

7 に答える 7

50

以下は、GAの完全な概要です。C / Java / Python /に簡単にコーディングできるように、非常に詳細に記述しました。

/* 1. Init population */
POP_SIZE = number of individuals in the population
pop = newPop = []
for i=1 to POP_SIZE {
    pop.add( getRandomIndividual() )
}

/* 2. evaluate current population */
totalFitness = 0
for i=1 to POP_SIZE {
    fitness = pop[i].evaluate()
    totalFitness += fitness
}

while not end_condition (best fitness, #iterations, no improvement...)
{
    // build new population
    // optional: Elitism: copy best K from current pop to newPop
    while newPop.size()<POP_SIZE
    {
        /* 3. roulette wheel selection */
        // select 1st individual
        rnd = getRandomDouble([0,1]) * totalFitness
        for(idx=0; idx<POP_SIZE && rnd>0; idx++) {
            rnd -= pop[idx].fitness
        }
        indiv1 = pop[idx-1]
        // select 2nd individual
        rnd = getRandomDouble([0,1]) * totalFitness
        for(idx=0; idx<POP_SIZE && rnd>0; idx++) {
            rnd -= pop[idx].fitness
        }
        indiv2 = pop[idx-1]

        /* 4. crossover */
        indiv1, indiv2 = crossover(indiv1, indiv2)

        /* 5. mutation */
        indiv1.mutate()
        indiv2.mutate()

        // add to new population
        newPop.add(indiv1)
        newPop.add(indiv2)
    }
    pop = newPop
    newPop = []

    /* re-evaluate current population */
    totalFitness = 0
    for i=1 to POP_SIZE {
        fitness = pop[i].evaluate()
        totalFitness += fitness
    }
}

// return best genome
bestIndividual = pop.bestIndiv()     // max/min fitness indiv

現在、適応度関数が欠落していることに注意してください(ドメインによって異なります)。クロスオーバーは、単純な1ポイントのクロスオーバーになります(バイナリ表現を使用しているため)。突然変異は、ランダムなビットの単純な反転である可能性があります。


編集:私はあなたの現在のコード構造と表記法を考慮して上記の擬似コードをJavaに実装しました(私はJavaよりもac / c ++の人であることに注意してください)。これは決して最も効率的または完全な実装ではないことに注意してください。私はそれをかなり迅速に書いたことを認めます。

個人.java

import java.util.Random;

public class Individual
{
    public static final int SIZE = 500;
    private int[] genes = new int[SIZE];
    private int fitnessValue;

    public Individual() {}

    public int getFitnessValue() {
        return fitnessValue;
    }

    public void setFitnessValue(int fitnessValue) {
        this.fitnessValue = fitnessValue;
    }

    public int getGene(int index) {
        return genes[index];
    }

    public void setGene(int index, int gene) {
        this.genes[index] = gene;
    }

    public void randGenes() {
        Random rand = new Random();
        for(int i=0; i<SIZE; ++i) {
            this.setGene(i, rand.nextInt(2));
        }
    }

    public void mutate() {
        Random rand = new Random();
        int index = rand.nextInt(SIZE);
        this.setGene(index, 1-this.getGene(index));    // flip
    }

    public int evaluate() {
        int fitness = 0;
        for(int i=0; i<SIZE; ++i) {
            fitness += this.getGene(i);
        }
        this.setFitnessValue(fitness);

        return fitness;
    }
}

Population.java

import java.util.Random;

public class Population
{
    final static int ELITISM_K = 5;
    final static int POP_SIZE = 200 + ELITISM_K;  // population size
    final static int MAX_ITER = 2000;             // max number of iterations
    final static double MUTATION_RATE = 0.05;     // probability of mutation
    final static double CROSSOVER_RATE = 0.7;     // probability of crossover

    private static Random m_rand = new Random();  // random-number generator
    private Individual[] m_population;
    private double totalFitness;

    public Population() {
        m_population = new Individual[POP_SIZE];

        // init population
        for (int i = 0; i < POP_SIZE; i++) {
            m_population[i] = new Individual();
            m_population[i].randGenes();
        }

        // evaluate current population
        this.evaluate();
    }

    public void setPopulation(Individual[] newPop) {
        // this.m_population = newPop;
        System.arraycopy(newPop, 0, this.m_population, 0, POP_SIZE);
    }

    public Individual[] getPopulation() {
        return this.m_population;
    }

    public double evaluate() {
        this.totalFitness = 0.0;
        for (int i = 0; i < POP_SIZE; i++) {
            this.totalFitness += m_population[i].evaluate();
        }
        return this.totalFitness;
    }

    public Individual rouletteWheelSelection() {
        double randNum = m_rand.nextDouble() * this.totalFitness;
        int idx;
        for (idx=0; idx<POP_SIZE && randNum>0; ++idx) {
            randNum -= m_population[idx].getFitnessValue();
        }
        return m_population[idx-1];
    }

    public Individual findBestIndividual() {
        int idxMax = 0, idxMin = 0;
        double currentMax = 0.0;
        double currentMin = 1.0;
        double currentVal;

        for (int idx=0; idx<POP_SIZE; ++idx) {
            currentVal = m_population[idx].getFitnessValue();
            if (currentMax < currentMin) {
                currentMax = currentMin = currentVal;
                idxMax = idxMin = idx;
            }
            if (currentVal > currentMax) {
                currentMax = currentVal;
                idxMax = idx;
            }
            if (currentVal < currentMin) {
                currentMin = currentVal;
                idxMin = idx;
            }
        }

        //return m_population[idxMin];      // minimization
        return m_population[idxMax];        // maximization
    }

    public static Individual[] crossover(Individual indiv1,Individual indiv2) {
        Individual[] newIndiv = new Individual[2];
        newIndiv[0] = new Individual();
        newIndiv[1] = new Individual();

        int randPoint = m_rand.nextInt(Individual.SIZE);
        int i;
        for (i=0; i<randPoint; ++i) {
            newIndiv[0].setGene(i, indiv1.getGene(i));
            newIndiv[1].setGene(i, indiv2.getGene(i));
        }
        for (; i<Individual.SIZE; ++i) {
            newIndiv[0].setGene(i, indiv2.getGene(i));
            newIndiv[1].setGene(i, indiv1.getGene(i));
        }

        return newIndiv;
    }


    public static void main(String[] args) {
        Population pop = new Population();
        Individual[] newPop = new Individual[POP_SIZE];
        Individual[] indiv = new Individual[2];

        // current population
        System.out.print("Total Fitness = " + pop.totalFitness);
        System.out.println(" ; Best Fitness = " + 
            pop.findBestIndividual().getFitnessValue());

        // main loop
        int count;
        for (int iter = 0; iter < MAX_ITER; iter++) {
            count = 0;

            // Elitism
            for (int i=0; i<ELITISM_K; ++i) {
                newPop[count] = pop.findBestIndividual();
                count++;
            }

            // build new Population
            while (count < POP_SIZE) {
                // Selection
                indiv[0] = pop.rouletteWheelSelection();
                indiv[1] = pop.rouletteWheelSelection();

                // Crossover
                if ( m_rand.nextDouble() < CROSSOVER_RATE ) {
                    indiv = crossover(indiv[0], indiv[1]);
                }

                // Mutation
                if ( m_rand.nextDouble() < MUTATION_RATE ) {
                    indiv[0].mutate();
                }
                if ( m_rand.nextDouble() < MUTATION_RATE ) {
                    indiv[1].mutate();
                }

                // add to new population
                newPop[count] = indiv[0];
                newPop[count+1] = indiv[1];
                count += 2;
            }
            pop.setPopulation(newPop);

            // reevaluate current population
            pop.evaluate();
            System.out.print("Total Fitness = " + pop.totalFitness);
            System.out.println(" ; Best Fitness = " +
                pop.findBestIndividual().getFitnessValue()); 
        }

        // best indiv
        Individual bestIndiv = pop.findBestIndividual();
    }
}
于 2009-10-16T01:14:06.527 に答える
4

JGAPのようなオープンソースフレームワークを使用してみませんか:http://jgap.sf.net

于 2010-02-16T15:00:41.743 に答える
3

「累積フィットネス配列」と二分探索を作成することでこのアルゴリズムを実装したため、選択中に配列内の各要素を反復処理する必要がなくなりました。

  1. 人口サイズ N の場合、累積フィットネス配列 arr[N] を作成します。
  2. arr[0] := computeFitness(individual[0]) を設定します。
  3. 次に、後続の各要素: X、arr[X] = arr[X-1] + computeFitness(individual[X])。
  4. 0 と arr[N] の間の乱数を生成します (つまり、合計フィットネス)。
  5. バイナリ検索 (Collections.binarySearch など) を使用して累積フィットネス配列内の適切なインデックスを見つけ、このインデックスを使用して個体を選択します。

再生フェーズの開始時にのみフィットネス配列を作成する必要があることに注意してください。その後、それを複数回再利用して、O(log N)時間で選択を実行できます。

余談ですが、トーナメントの選択は実装がはるかに簡単です。

于 2009-10-15T21:43:11.667 に答える
1

私は Java で拡張可能な実装を作成しました。この実装では、オペレーターと個々の構造は、連携して機能するインターフェースによって明確に定義されています。Github リポジトリはこちらhttps://github.com/juanmf/ga

これには、各オペレーターの標準的な実装と、特定の個人/集団構造とフィットネス メーターを使用した問題の実装例があります。問題の実装例は、20 のチームと予算制限の中から選手がいる良いサッカー チームを見つけることです。

現在の問題に適応させるには、これらのインターフェースの実装を提供する必要があります。

juanmf.ga.structure.Gen;
juanmf.ga.structure.Individual;
juanmf.ga.structure.IndividualFactory; 
juanmf.ga.structure.Population;  // Has a basic implementation already
juanmf.ga.structure.PopulationFactory;

以下pkg juanmf.grandtのコード スニペットに示すように、問題の実装クラスの例と、それらを公開する方法があります。

実装を公開するには、この Spring Bean から適切なクラスを返すだけです。

/**
 * Make sure @ComponentScan("pkg") in juanmf.ga.App.java includes this class' pkg 
 * so that these beans get registered.
 */
@Configuration
public class Config {

    @Bean(name="individualFactory")
    public IndividualFactory getIndividualFactory() {
        return new Team.TeamFactory();
    }

    @Bean(name="populationFactory")
    public PopulationFactory getPopulationFactory() {
        return new Team.TeamPopulationFactory();
    }

    @Bean(name="fitnessMeter")
    public FitnessMeter getFitnessMeter() {
        return new TeamAptitudeMeter();
    }
} 

Crosser オペレーターには、同じ手法に対して 2 つの実装があります。1 つは順次で、もう 1 つは並行であり、順次よりもはるかに優れています。

停止条件を指定できます。何も指定されていない場合、100 世代後に改善されずに停止するデフォルトの停止条件があります (ここでは、この停止条件を効果的にトリガーするために、各世代の最善を失わないように、エリート主義者に注意する必要があります)。

ですので、どなたか挑戦してみたいという方がいらっしゃれば、喜んでお手伝いさせていただきます。誰でも提案を提供することを歓迎します。さらに良いことに、オペレーターの実装:Dまたはプルリクエストを改善します。

于 2015-09-28T14:06:44.370 に答える
1

あなたが探している概念は、「ルーレット盤の選択」と呼ばれます。フィットネス関数はまだ確立されていませんが (各個人のフィットネスがその染色体の積分値であることを暗示している可能性があります)、一般的な計画を実行すると、次のようになります。

  1. 母集団全体の適合度を合計します。
  2. 0 と合計フィットネスの間の乱数 (x と呼びます) を取得します。
  3. 母集団を反復処理します。各メンバーについて:
    1. x からメンバーのフィットネスを引きます。
    2. x が 0 以下になった場合は、現在のメンバーを選択します。

他にも同等の実装がありますが、一般的な考え方は、適合度に比例する確率でメンバーを選択することです。

編集:フィットネス機能に関するいくつかのメモ。染色体の表現 (この場合は 32 ビット整数) は、それを評価するために使用されるフィットネス関数とは無関係です。たとえば、バイナリ エンコーディングは通常、適切なサイズの整数値にパックされたビットフィールドのセットとして染色体を扱います。クロスオーバーと突然変異は、適切なビットマスキング操作によって実現できます。興味があれば、ビット単位の操作を使用してこれらの関数を実装する (C および Python で) 簡単な GA コードを投稿できます。あなたがこれらの言語にどれほど慣れているかはわかりません。

于 2009-10-15T21:14:29.543 に答える
0

ルーレットホイールの選択に関するこれらの他の質問は、役立つはずです。

最初のものでは、ルーレット盤がどのように機能するかを説明しようとしました。2番目に、JarodElliottがいくつかの擬似コードを提供しました。効率的な実装に関するAdamskiの説明と組み合わせると、これらは何かを機能させるのに十分なはずです。

于 2009-10-16T00:17:58.383 に答える
0

言及する価値のあるポイントです。ルーレット ホイールの選択 (疑似コードで示されているように) は、最小化の問題では機能しませんが、最大化の問題では有効です。

最も適合する個体が選択される方法により、最小化のケースは成立しません。詳細は、Computational Intelligence: 2nd editionで提供されています。

于 2010-02-13T06:44:36.343 に答える