pyevolveによる遺伝的アルゴリズム(3)

pyevolveによる遺伝的アルゴリズム(1)
pyevolveによる遺伝的アルゴリズム(2)


今回はGAで「巡回セールスマン問題」を解いてみます。
「matplotlib」のインタラクティブモードを使い、巡回経路がリアルタイムで変化するようにしました、経路がウネウネと最適化されていく姿を見てるだけでも面白いです。



実行には「numpy」「pyevolve」「matplotlib」の3つのライブラリが必要です、以下のサイトからダウンロードしてください。


http://numpy.scipy.org/
http://pyevolve.sourceforge.net/
http://matplotlib.sourceforge.net/


拠点数、世代数、標本数、突然変異確率をいじると、答えの導き方が変わってくるので、いろいろいじるのも楽しいです。


色々な経路を解いていると解ってくるのですが、拠点数が多くなってくると最適解まで届かない場合が多いです。遺伝子の袋小路に入ってしまうため、突然変異によるブレイクスルー待ちの状態になってしまうのですね。
ああ、ここを直せばいいのにぃ・・・とヤキモキしたりして、子供を見守る親の気持ちになったりします。


GAが最適解に向かわない問題の解決策としては、評価関数をもっと頭よくする必要があります。
状態によって評価関数を差し替えるの手段も有効です。
この巡回問題ならば「経路が交差している場合はペナルティ」などの処理を入れると、もっと精度が上がりそうな気がします。

#coding:utf-8
from pyevolve import GAllele
from pyevolve import G1DList
from pyevolve import Mutators
from pyevolve import Crossovers
from pyevolve import GSimpleGA
from pyevolve import Consts
from pylab import *
from math import sqrt
import random

#設定値
N               = 30    #拠点数
GENERATIONS     = 1000   #世代数
POPULATION_SIZE = 100    #標本数   
CROSSOVER_RATE  = 1.0    #交叉確率(0.0〜1.0)
MUTATION_RATE   = 0.08   #突然変異確率(0.0〜1.0)

#グローバル変数
city_x     = randn(N)    #拠点X座標
city_y     = randn(N)    #拠点Y座標
city_dists = []          #距離表
plot_line  = None        #Plot-line
best_chromosome = None   #最高結果

def create_distmatrix():
    """距離表の作成"""
    global city_dists
    for i in range(N):
        city_dists.append([])
        for j in range(N):
            dx = city_x[i]-city_x[j]
            dy = city_y[i]-city_y[j]
            city_dists[i].append(sqrt(dx**2 + dy**2))

def evolve_callback(ga_engine):
    """1ステップごとのコールバック"""
    global best_chromosome
    cur = list(ga_engine.bestIndividual())
    if best_chromosome != cur:
        best_chromosome = cur
        line_plot.set_xdata([city_x[t] for t in best_chromosome])
        line_plot.set_ydata([city_y[t] for t in best_chromosome])
        draw()

def eval_func(chromosome):
    """評価関数"""
    total=0
    num_cities = len(chromosome)
    for i in range(num_cities):
        j=(i+1)%num_cities
        city_i=chromosome[i]
        city_j=chromosome[j]
        total+=city_dists[city_i][city_j]
    return total

def G1DListTSPInitializator(genome, **args):
    """ 初期化関数 """
    genome.clearList()
    lst = [i for i in xrange(genome.listSize)]
    
    for i in xrange(genome.listSize):
        choice = random.choice(lst)
        lst.remove(choice)
        genome.append(choice)

if __name__ == "__main__":

    create_distmatrix()
    
    #グラフの設定
    ion()       # matplotlib - set intarctive mode
    line_plot, = plot(city_x,city_y,'r', lw=3,zorder=1)
    scatter(city_x,city_y,marker='+', c='r',zorder=2) 
    draw()

    #対立遺伝子(Allele)の設定
    setOfAlleles = GAllele.GAlleles(homogeneous=True)
    a = GAllele.GAlleleList([ i for i in xrange(N) ])
    setOfAlleles.add(a)

    #GAの設定
    genome = G1DList.G1DList(N)
    genome.setParams(allele=setOfAlleles)
    genome.evaluator.set(eval_func)
    genome.mutator.set(Mutators.G1DListMutatorSwap)
    genome.crossover.set(Crossovers.G1DListCrossoverOX)
    genome.initializator.set(G1DListTSPInitializator)
    ga = GSimpleGA.GSimpleGA(genome)
    ga.setGenerations(GENERATIONS)
    ga.setPopulationSize(POPULATION_SIZE)
    ga.setCrossoverRate(CROSSOVER_RATE)
    ga.setMutationRate(MUTATION_RATE)
    ga.stepCallback.set(evolve_callback)
    ga.setMinimax(Consts.minimaxType["minimize"])

    #GA実行
    ga.evolve(freq_stats=100)
    
    #グラフ描画
    evolve_callback(ga)
    show()