Clinton Sheppard的Genetic Algorithms with Python一书总结的笔记
git链接:
https://github.com/handcraftsman/GeneticAlgorithmsWithPython
1. 用遗传算法guess password
import random
import datetime
geneSet = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!。"
target = "Hello World!"
def generate_parent(length):
genes = []
while len(genes) < length:
sampleSize = min(length - len(genes), len(geneSet))
genes.extend(random.sample(geneSet, sampleSize))
return ''.join(genes)
随机生成父母, random.sample(geneSet, sampleSize)
在geneSet里随机选sampleSize个不同的元素,组成字符串返回
def get_fitness(guess):
return sum(1 for expected, actual in zip(target, guess) if expected ==actual)
计算和目标相同的元素数量,作为适应度返回
def mutate(parent):
index = random.randrange(0, len(parent))
childGenes = list(parent)
newGene, alternate =random.sample(geneSet, 2)
childGenes[index] = alternate if newGene == childGenes[index] else newGene
return ''.join(childGenes)
变异,从parent里面随机选一位变异,产生newGene和alternate两个元素,是为了防止变异前和变异后相同
def display(guess):
timeDiff = datetime.datetime.now() - startTime
fitness = get_fitness(guess)
print("{}\t{}\t{}\t".format(guess, fitness, timeDiff))
random.seed()
startTime = datetime.datetime.now()
bestParent = generate_parent(len(target))
bestFitness = get_fitness(bestParent)
display(bestParent)
while True:
child = mutate(bestParent)
childFitness = get_fitness(child)
if bestFitness >= childFitness:
continue
display(child)
if childFitness >= len(bestParent):
break
bestFitness = childFitness
bestParent = child
2. 改进一:从上面提取一个可重复使用的engine
为了让这个GA engine不仅仅只解决这个项目,要单独把初始值的产生,变异,遗传算法的主要过程提取出来单独放到一个py文件,命名为genetic.py
import random
def _generate_parent(length, geneSet):
genes = []
while len(genes) < length:
sampleSize = min(length - len(genes), len(geneSet))
genes.extend(random.sample(geneSet, sampleSize))
return ''.join(genes)
def _mutate(parent, geneSet):
index = random.randrange(0, len(parent))
childGenes = list(parent)
newGene, alternate =random.sample(geneSet, 2)
childGenes[index] = alternate if newGene == childGenes[index] else newGene
return ''.join(childGenes)
def get_best(get_fitness, targetLen, optimalFitness, geneSet, display):
random.seed()
bestParent = _generate_parent(targetLen, geneSet)
bestFitness = get_fitness(bestParent)
display(bestParent)
if bestFitness >= optimalFitness:
return bestParent
while True:
child = _mutate(bestParent, geneSet)
childFitness = get_fitness(child)
if bestFitness >= childFitness:
continue
display(child)
if childFitness >= optimalFitness:
return child
bestFitness = childFitness
bestParent = child
适应度的计算,区间上下界以及主函数等放在另一个文件'guessword.py'
import datetime
import genetic
def get_fitness(genes, target):
return sum(1 for expected, actual in zip(target, genes) if expected ==actual)
def display(genes, target, startTime):
timeDiff = datetime.datetime.now() - startTime
fitness = get_fitness(genes, target)
print("{}\t{}\t{}\t".format(genes, fitness, timeDiff))
def test_Hello_World():
target = "Hello World!"
guess_password(target)
def guess_password(target):
geneSet = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!."
startTime = datetime.datetime.now()
def fnGetFitness(genes):
return get_fitness(genes, target)
def fnDisplay(genes):
display(genes, target, startTime)
optimalFitness = len(target)
genetic.get_best(fnGetFitness,len(target),optimalFitness,geneSet,fnDisplay)
if __name__ =='__main__':
test_Hello_World()
3. 改进二: Use unittest framework
在测试环境下工作,新建一个guessPasswordTests.py
import unittest
class GuessPasswordTests(unittest.TestCase):
geneSet = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!."
def test_Hello_World():
target = "Hello World!"
guess_password(target)
def guess_password(self, target):
optimalFitness = len(target)
best = genetic.get_best(fnGetFitness, len(target), optimalFitness, self.geneset, fnDisplay)
self.assertEqual(best, target)
if __name__ == '__main__':
unittest.main()
4.改进三: 认识染色体(Chromosome)
在genetic.py
定义一个染色体
class Chromosome:
Genes = None
Fitness = None
def __init__(self, genes, fitness):
self.Genes = genes
self.Fitness = fitness
加入定义的染色体后其它function的变化
def _generate_parent(length, geneSet, get_fitness):
genes = []
while len(genes) < length:
sampleSize = min(length - len(genes), len(geneSet))
genes.extend(random.sample(geneSet, sampleSize))
genes = ''.join(genes)
fitness = get_fitness(genes)
return Chromosome(genes, fitness)
def _mutate(parent, geneSet, get_fitness):
index = random.randrange(0, len(parent.Genes))
childGenes = list(parent.Genes)
newGene, alternate = random.sample(geneSet, 2)
childGenes[index] = alternate if newGene == childGenes[index] else newGene
genes = ''.join(childGenes)
fitness = get_fitness(genes)
return Chromosome(genes, fitness)
def get_best(get_fitness, targetLen, optimalFitness, geneSet, display):
random.seed()
bestParent = _generate_parent(targetLen, geneSet, get_fitness)
display(bestParent)
if bestParent.Fitness >= optimalFitness:
return bestParent
while True:
child = _mutate(bestParent, geneSet, get_fitness)
if bestParent.Fitness >= child.Fitness:
continue
display(child)
if child.Fitness >= optimalFitness:
return child
bestParent = child
5. 改进四: 基准测试(Benchmarking)
在genetic.py
添加Benchmarking来了解一个算法求解要多久,标准偏差是多少
import statistic
import time
class Benchmark:
@staticmethod
def run(function):
timings = []
stdout = sys.stdout
for i in range(100):
sys.stdout = None
startTime = time.time()
function()
seconds = time.time() - startTime
sys.stdout = stdout
timings.append(seconds)
mean = statistics.mean(timings)
if i < 10 or i % 10 == 9:
print("{} {:3.2f} {:3.2f}".format(
1 + i, mean,
statistics.stdev(timings, mean) if i > 1 else 0))
在guessPasswordTests.py
中添加相关function
import datetime
import random
import unittest
import genetic
def get_fitness(guess, target):
return sum(1 for expected, actual in zip(target, guess)
if expected == actual)
def display(candidate, startTime):
timeDiff = datetime.datetime.now() - startTime
print("{}\t{}\t{}".format(
candidate.Genes, candidate.Fitness, timeDiff))
class GuessPasswordTests(unittest.TestCase):
geneset = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!.,"
def test_Hello_World(self):
target = "Hello World!"
self.guess_password(target)
def test_For_I_am_fearfully_and_wonderfully_made(self):
target = "For I am fearfully and wonderfully made."
self.guess_password(target)
def guess_password(self, target):
startTime = datetime.datetime.now()
def fnGetFitness(genes):
return get_fitness(genes, target)
def fnDisplay(candidate):
display(candidate, startTime)
optimalFitness = len(target)
best = genetic.get_best(fnGetFitness, len(target), optimalFitness,
self.geneset, fnDisplay)
self.assertEqual(best.Genes, target)
def test_Random(self):
length = 150
target = ''.join(random.choice(self.geneset)
for _ in range(length))
self.guess_password(target)
def test_benchmark(self):
genetic.Benchmark.run(self.test_Random)
if __name__ == '__main__':
unittest.main()