天天看點

python遺傳算法執行個體:求一進制二次方程執行個體1. 項目簡介2. 遺傳算法3. 示範4. 代碼講解5. 完整代碼

文章目錄

  • 1. 項目簡介
    • 1.1 解的編碼
    • 1.2 解的交叉融合
    • 1.3 突變
    • 1.4 适合度計算
  • 2. 遺傳算法
  • 3. 示範
    • 3.1 細節模式
    • 3.2 非細節模式
  • 4. 代碼講解
    • 4.1 NumberSpecies類
    • 4.2 Population類
      • 4.2.1 live()
      • 4.2.2 nextGeneration ()
      • 4.2.3 sortByFitness ( fitnessList, speciesList)
      • 4.2.4 getChildPopulation ()
      • 4.2.5 getChild (f, m)
      • 4.2.6 selectParent ()
  • 5. 完整代碼

1. 項目簡介

利用遺傳算法(Genetic Algorithm, GA)建構了一個求一進制二次方程近似解的項目。

語言:python 3.6.8

作業系統:win10

所用的python包:

  1. random
  2. matplotlib
  3. re
  4. time

1.1 解的編碼

這裡将解表示為二進制代碼,預設定義一共有24位,前8位是解的整數位,第一位是表示正負的符号位(0-負,1-正),後16位是解的小數位,整個解的搜尋空間是

[+127.65535, -127.65535]

python遺傳算法執行個體:求一進制二次方程執行個體1. 項目簡介2. 遺傳算法3. 示範4. 代碼講解5. 完整代碼

I N T = 2 7 + 2 6 + 2 5 + . . . + 2 0 = 127 F l o a t = 2 15 + 2 14 + 2 13 + . . . + 2 0 = 65535 INT = 2^7+2^6+2^5+...+2^0=127 \\ Float= 2^{15}+2^{14}+2^{13}+...+2^0=65535 INT=27+26+25+...+20=127Float=215+214+213+...+20=65535

1.2 解的交叉融合

子解,也即子代的編碼來自于适合度較高的一對父/母解,子解的每一位通過随機的方式來自于父解或母解。這裡預設選擇适合度(升序)排名前20的解兩兩随機配對組成父母解,每一對父母可以生産10個子解。

python遺傳算法執行個體:求一進制二次方程執行個體1. 項目簡介2. 遺傳算法3. 示範4. 代碼講解5. 完整代碼

注:圖中僅标注了父母解同位不同編碼情況下子解的編碼來源,實際上是每一位都要随機標明來自父/母解。

1.3 突變

這裡為了增大突變的差異,預設定義了發生一次突變一次性更改子解的任意五個位,變為與之前相反的數字,比如0變1,1變0。

1.4 适合度計算

T a r g e t = T ( x ) = c o e f 1 ∗ x 2 + c o e f 2 ∗ x F i t n e s s = ( T a r g e t − T ( x 1 ) ) 2 Target = T(x) = coef_1*x^2+coef_2*x \\ Fitness = (Target - T(x_1))^2 Target=T(x)=coef1​∗x2+coef2​∗xFitness=(Target−T(x1​))2

coef1 和 coef2 是系數常量,Target 也是一個常量 由于我建構遺傳的目的是要逼近一進制二次方程的一個近似解,是以這裡的适合度我定義作與 Target 的誤差平方和,且适合度越小的個體,存活率越高。

2. 遺傳算法

遺傳算法的具體介紹可以參考:《遺傳算法(Genetic Algorithm)從了解到執行個體運用(上)(python)》

這裡我簡單概括一下他的特點和我的了解。

遺傳算法是定向随機優化的一種,需要實作它包含了章節1中的4個特點:1. 編碼;2. 交叉融合;3. 突變;4. 适合度定義。适合度是進化的方向,好的适合度定義對遺傳算法來說至關重要,我們通過适合度優勝劣汰,擇取“優質”的個體,兩兩配對通過交叉融合和突變産生新的群體,又再不斷篩選适合度高的個體進行配對産生下一代,如此反複,達到适合的疊代次數後便能産生得到符合我們預期的群體。

遺傳算法展現了“物競天擇,适者生存”的哲學觀點。一般來說,雖然大家都說遺傳算法是全局尋優的,可是我并不這麼認為,在交叉融合和突變過程中如果操作人不當的安排是達不到全局尋優的,這裡我的安排還是有缺陷的,我是選擇适合度排名前20的個體任意兩兩配對,每對父母産生10個子解,當适合度的最高的物種的數量較少時,我這麼安排會有很大的可能性導緻由于交叉融合和突變的子代喪失了父母的高适合度,進而湮滅在浩蕩的種群生存曆史中。比較好的做法應該是直接複制多個适合度最高的個體編碼賦予子代,直接保留父母的特性。

另外遺傳算法的搜尋在解空間的跳躍距離也是達到全局尋優的關鍵,遺傳算法的跳躍能力取決于交叉融合和突變的政策。這裡我做的交叉融合政策更像“同位繼承”,在一定的世代疊代後,我們的群體适合度往往相差無幾,甚至具有同一編碼,那麼在是“同位繼承”下是得不到具有差異的子代的,甚至直接是父母的副本,群體到了這一步,隻能依靠發揮不太穩定的突變了,如果依照我目前的交叉融合政策,父母的組成上我需要保證他們的多樣性,很可惜,我并沒有,我也是後知後覺,但是還好腳本運作正常,可以得到最優解的近似解。另外,在交叉融合上還有其他更花的方式,擴充到真正的生物體染色體上,簡直就是變态,比如,直接把編碼倒位,父母解的編碼不同部分任意融合等,然而真正的生物體上的染色體确實存在交叉互換,倒位,易位,缺失等,不過機率比較低。 是以,在交叉融合和突變的政策上需要保證産生的子代的多樣性的同時還要保證子代于與父母的不同,同時又要通過産生和父母編碼相同的個體來保留父母的特性來延續群體,而編碼有差異的個體就是開拓的先鋒,群體通過這些差異個體來尋找更好的編碼,進而更好的适應環境。群體浩浩蕩蕩,經久不衰的生存史,是建立在不少先輩英勇犧牲上的,我們當下的幸福是一位位革命先烈和為祖國事業做出偉大貢獻的人流血拼命換來的,希望諸君,勤學好問,砥砺前行,将來輪到我們站出來時,有能力也有血氣的肩住黑暗的閘門放下一輩到寬闊光明的地方去,希望每個人都能盡一份光與熱,螢火便發一份螢火的光,若世上沒有火炬我便是唯一的光。

3. 示範

細節模式和非細節模式的控制在于

DETAIL

變量的

False

True

if __name__ == "__main__": 
	# Define a express
	f="1000=12x**2+25x"
	
	# Individual code information
	XINTSIZE = 9
	XDECSIZE = 16
	
	# Population information
	POPULATION_SIZE = 100
	CHILD_SIZE = 10
	MAX_GENERATION_SIZE = 50
	MUTATE_SIZE = 5
	MUTATE_RATE = 0.05
	CROSS_MERGE_RATE = 0.5
	
	# visualization detail
	X_RANGE = [-100,100]
	Y_RANGE = [-500,6000]
	
	
	# Print detail
	DETAIL = False
	PAUSE = True
	
	# Create a population
	numPopulation = Population(
		popSize = POPULATION_SIZE,
		childsSize = CHILD_SIZE,
		maxGenerationSize = MAX_GENERATION_SIZE,
		formulate=f,
		)
	
	numPopulation.live()
	
	# visualization
	if not DETAIL:
		numPopulation.visualization()
	else:
		numPopulation.visualizationDetail()
		
	print("-"*80)
	best = numPopulation.species[0]
	bestX = best.getDecimal()
	bestTarget = numPopulation.getTarget(bestX)
	print("Best x = %.3f"%bestX)
	print("Best Target = %.3f"%bestTarget)
	print("%.3f = %.3fx**2 + %.3fx"%(numPopulation.target,numPopulation.xCoef,numPopulation.yCoef))
           

這裡示範了整數位長9位,小數位長16位,群體大小為100,每對父母産生10個子代,最大世代為50代,突變規模為一次5位,突變率為0.05,交叉融合來自父/母各一半的機率下,一進制二次方程:

1000 = 12 ∗ x 2 + 25 x 1000=12*x^2+25x 1000=12∗x2+25x

下的腳本輸出

3.1 細節模式

列印每個世代前20個體的資訊。最後會可視化每一代的取值情況。

控制台列印

python遺傳算法執行個體:求一進制二次方程執行個體1. 項目簡介2. 遺傳算法3. 示範4. 代碼講解5. 完整代碼

可視化世代取值

python遺傳算法執行個體:求一進制二次方程執行個體1. 項目簡介2. 遺傳算法3. 示範4. 代碼講解5. 完整代碼

可見大概五代左右就出現了兩個最優解,可是其中一個解随着世代的進行,慢慢消失了。

3.2 非細節模式

非細節模式列印下,會計算每個世代前20個個體的平均值并列印輸出。

python遺傳算法執行個體:求一進制二次方程執行個體1. 項目簡介2. 遺傳算法3. 示範4. 代碼講解5. 完整代碼

4. 代碼講解

這裡主要講解了代碼部分我認為比較重要的地方以及具體的流程

4.1 NumberSpecies類

他是解的定義,内部擁有一個

code

清單變量,預設情況下有24位,即是解的編碼儲存,一共擁有5個方法,具體的介紹可見對應代碼塊中的注釋部分。他是解個體在代碼中的容器。

4.2 Population類

他是群體,通過

species

清單變量管理每個個體,這些個體是

NumberSpecies

類的執行個體。其中

live

API是管理整個群體疊代的部分,它是一個循環,定義達到最大世代數為止。

4.2.1 live()

def live(self):
		"""
		群體生存周期
		"""
		while self.generation < self.maxGenerationSize:
			self.nextGeneration()
           

4.2.2 nextGeneration ()

def nextGeneration(self):
		"""
		産生下一個世代
		"""
		# calculate current generation fitness
		fitnessList = self.calFitness(self.species)
		
		# order by fitness
		self.sortByFitness(fitnessList, self.species)
		
		#Survival individual
		# child born
		childs = self.getChildPopulation()

		#choose survival child as next generation
		fitnessListChild = self.calFitness(childs)
		self.sortByFitness(fitnessListChild, childs)
		self.species = childs[:self.popSize]
		
		#
		self.generation += 1
		
		#
		self.show(detail = DETAIL)
           

這是遺傳算法的處理邏輯,如下:

  1. 計算目前群體适合度并排序;
  2. 選中的個體生殖産生子代群體;
  3. 計算子代群體的适合度并排序;
  4. 将對應大小的子代群體替換掉目前群體,世代數加1;
  5. 列印目前世代群體資訊;

4.2.3 sortByFitness ( fitnessList, speciesList)

def sortByFitness(self, fitnessList, speciesList):
		"""
		根據适合度排序,選擇排序,升序
		"""
		L = len(fitnessList)
		for i in range(L):
			for j in range(i,L):
				if fitnessList[i] > fitnessList[j]:
					fitnessList[i], fitnessList[j] = fitnessList[j], fitnessList[i]
					speciesList[i], speciesList[j] = speciesList[j], speciesList[i]
           

通過冒泡排序,按照适合度從小到大的順序排列個體和适合度清單。

4.2.4 getChildPopulation ()

def getChildPopulation(self):
		"""
		子代出生
		return child
		"""
		# selectParent
		fathersI, mothersI = self.selectParent()
		L = len(fathersI)
		
		# get childs
		childs = []
		for i in range(L):
			for j in range(self.childsSize):
				# child born
				fI = fathersI[i]
				mI = mothersI[i]
				child = self.getChild(self.species[fI], self.species[mI])
				
				# add child to child population
				childs.append(child)
				
		return childs
           

預設選擇适合度排名前20的個體随機兩兩組成一對父母,傳回父親個體所在

species

變量的索引清單

fathersI

,母親類似。然後每對父母依次通過交叉融合和突變後得到10個孩子,添加到子代群體中并傳回子代群體。

4.2.5 getChild (f, m)

def getChild(self,f,m):
		"""
		1.二進制編碼交叉融合
		2.突變編碼
		單個孩子
		"""
		assert isinstance(f,NumberSpecies),"crossMerge(f,m) f and m must be NumberSpecies class"
		assert isinstance(m,NumberSpecies),"crossMerge(f,m) f and m must be NumberSpecies class"
		
		seed = random.uniform(0,1)
		
		# do crossMerge?
		# decide cross position
		childCode = []
		for i in range(f.totalSize):
			fromSeed = random.uniform(0,1)
			if fromSeed > self.crossMergeRate:
				childCode.append(f.code[i])
			else:
				childCode.append(m.code[i])
			
		
		# do mutate?
		# randomly choose x position to mutate
		if seed < self.mutateRate:
			tempPosIndex = [i for i in range(f.totalSize)]
			mutatePos = random.sample(tempPosIndex,self.mutatePosSize)
			
			# Change code
			for i in mutatePos:
				if childCode[i] == 0:
					childCode[i] = 1
					
				else:
					childCode[i] = 0
					
		# child born
		child = NumberSpecies(XINTSIZE,XDECSIZE)
		child.assignCode(childCode)

		return child
           

輸入父母執行個體。随機數的産生通過

random.uniform (0, 1)

從一個0到1均等分布中抽取。預設如果大于0.5則來自父方的編碼位,否則來自母方。如果産生的随機數預設小于0.05則,任意突變5個編碼位。将編碼直接賦予

NumberSpecies

新的執行個體,并作為單個子代放回。

4.2.6 selectParent ()

def selectParent(self,size = 20):
		"""
		預設選擇适合度排名前20的父母,随機兩兩配對
		return index list of select species one is father another is mother 
		"""
		assert size < len(self.species), "selectParent Func size=%d par must less population%d"%(size, len(self.species))
		
		# get size of couple
		coupleSize = size // 2
		
		# get total index of couple
		total = set([i for i in range(coupleSize*2)])
		
		# father and mother 
		father = set(random.sample(total,coupleSize))
		mother = list(total - father)
		mother = random.sample(mother,coupleSize)
		father = random.sample(father,coupleSize)
		
		
		return father, mother
           

父母的随機配置設定通過

random.sample (population, times)

随機從population中抽取times個樣本,作為父母的索引清單傳回。

5. 完整代碼

#python3

#--------------------------
# Author: little shark
# Date: 2022/4/13
"""
遺傳算法求一進制二次方程
"""

import random
from matplotlib import pyplot as plt
from time import sleep
import re


def getRange(x,y):
	"""
	x: x^2的系數
	y: x的系數
	
	傳回可視化的取值範圍[-50, 50]
	"""
	
	targets = []
	scale = [i for i in range(X_RANGE[0], X_RANGE[1], 1)]
	for i in scale:
		t = x * i**2 + y*i
		targets.append(t)
		
	return targets
	

class NumberSpecies:
	"""
	定義了一個逼近一進制二次方程解的物種
	"""
	def __init__(self, xIntSize = 8, xDecSize = 16):
		"""
		xIntSize: x元的二進制編碼整數位數,預設8位,第一位是正負位
		xDecSize: x元的二進制編碼小數位數,預設16位
		
		預設一共24位,排列如下
		8位x元整數位  16位x元小數位
		"""
		# define a bit size to x 
		self.xIntSize = xIntSize
		
		# define a bit size to decimal
		self.xDecSize = xDecSize
		
		# total size
		self.totalSize = xIntSize + xDecSize
		
		# define code
		self.code=[0 for i in range(self.totalSize)]
		
		# random it code
		self.random()

		
	def assignCode(self,code):
		"""
		直接賦予數字物種二進制代碼
		"""
		self.code = code.copy()
	
	def show(self):
		"""
		列印二進制編碼資訊及其對應的x元,y元的十進制
		"""
		print("code    = ",self.code)
		print("numb    = ",self.getDecimal())
		#print("fitness = ",self.fitness)
	
	def random(self):
		"""
		Ramdom code
		随機其編碼
		"""
		self.code=[random.choice([0,1]) for i in range(self.totalSize)]
		
	def getDecimal(self):
		"""
		turn code into decimal
		将二進制編碼轉為十進制
		"""
		
		#---------------------------------
		# X part
		# part of x int
		xIntNum = 0
		start = 1
		signXIndex = 0
		end = self.xIntSize
		xIntCode = self.code[start: end]
		for i in range(self.xIntSize-1):
			xIntNum += pow(2,self.xIntSize - i - 1)*xIntCode[i]
		
		
		# part of x decimal
		xDecNum = 0
		start = end
		end = end + self.xDecSize
		xDecCode = self.code[start: end]
		for i in range(self.xDecSize):
			xDecNum += pow(2,self.xDecSize - i - 1)*xDecCode[i]
		
		
		# x str -> float
		xDecStr = str(xIntNum) + "." + str(xDecNum)
		xFinalNum = float(xDecStr)
		if self.code[signXIndex] == 0:
			xFinalNum *= -1
		
		
		return xFinalNum
			
	
	def codeClone(self):
		"""
		return a code clone
		"""
		return self.code.copy()
	

class Population:
	"""
	管理NumberSpecies:
		1.控制物種突變;
		2.控制交叉融合;
		3.計算适合度;
		4.不斷更新群體,淘汰适合度低的物種,選擇适合度高的父/母本進行配對,誕生下一代。
		
	"""
	def __init__(self, popSize = 100, childsSize = 4, maxGenerationSize = 50, formulate = "" ):
		"""
		popSize: 群體大小
		childsSize: 每對夫婦的子女數
		maxGenerationSize: 最大世代數
		formulate: 公式 constant = c1*x**n + c2*y
		"""
		
		# Is expression legal?
		assert formulate != "", "You must input a formulate like target = c1*x**n + c2*y"
		
		# Extract information from formulate express
		m = re.search("([\d\.-]+)=([\d\.-]+)x\*\*2\+([\d\.-]+)x",formulate)
		
		# Is formulate legal?
		assert m != None,"Your formulate is not legal like: target = c1x**n + c2y "
		
		# Assign, formulate
		self.target = float(m.group(1))
		self.xCoef = float(m.group(2))
		self.xPower = 2
		self.yCoef = float(m.group(3))
		
		
		# Define the index of current generation
		self.generation = 0
		
		# Assign, generation information
		self.popSize = popSize
		self.childsSize = childsSize
		self.maxGenerationSize = maxGenerationSize
		
		# Assign, about mutate and cross merge
		self.mutateRate = MUTATE_RATE
		self.crossMergeRate = CROSS_MERGE_RATE
		self.mutatePosSize = MUTATE_SIZE # mutate code number default 3
		
		
		# Preduce population
		self.species = [NumberSpecies(xIntSize = XINTSIZE, xDecSize = XDECSIZE) for i in range(popSize)]
		
		# history of population: fitness & x & y & predict constant "Target"
		self.historicalMeanFitness = []
		self.historicalMeanXDecimal = []
		self.historicalMeanYDecimal = []
		self.historicalMeanTargetDecimal = []
		
		self.XhistoricalTopPopulation = []
		self.TargethistoricalTopPopulation = []
		
		
	def getTop20Mean(self,xDecimalList,fitnessList):
		"""
		計算适合度排名前20物種的x和y元的平均系數以及适合度并傳回
		return average of coef about x & y and its target by define express
		"""
		assert len(xDecimalList) > 20, "Your population must more than 20"
		
		meanXDecimal = sum(xDecimalList[:20])/len(xDecimalList[:20])
		meanfitness = sum(fitnessList[:20])/len(fitnessList[:20])
		
		return meanXDecimal, meanfitness
	
	def calFitness(self, speciesList):
		"""
		計算适合度 (與定義的目标數字的誤差平方)
		"""
		fitnessList = []
		for i,num in enumerate(speciesList):
			xDecNum = num.getDecimal()
			
			# get fitness base on express
			fitness = (self.target - self.getTarget(xDecNum)) ** 2
			
			# save
			fitnessList.append(fitness)
		
		return fitnessList
	
	def sortByFitness(self, fitnessList, speciesList):
		"""
		根據适合度排序,選擇排序,升序
		"""
		L = len(fitnessList)
		for i in range(L):
			for j in range(i,L):
				if fitnessList[i] > fitnessList[j]:
					fitnessList[i], fitnessList[j] = fitnessList[j], fitnessList[i]
					speciesList[i], speciesList[j] = speciesList[j], speciesList[i]
	
	
	def getTarget(self,x):
		"""
		根據公式和給出的x的系數計算其結果。
		"""
		return self.xCoef * x ** self.xPower + self.yCoef * x
	
	def show(self, top=20, detail = False, pause = False):
		"""
		列印世代Top N的資訊:包括:世代索引,适合度,x,預測的結果,真實的結果
		有兩個模式:細節模式和非細節模式
			細節模式:detail = True
				列印Top N 物種的上述資訊
			非細節模式:datail = False
				列印Top 20 物種的上述資訊的平均值
				可以進行可視化,會儲存種群進化過程中的世代資訊
		"""
		# Get decimal of x and y
		xDecNums = []
		for i in self.species:
			xDecNum = i.getDecimal()
			xDecNums.append(xDecNum)
			
		# Get fitness
		fitnessList = self.calFitness(self.species)
		
		# Start show information
		if detail:
			print()
			print("="*80)
			print(" "*30,"*- Top %d -*"%top)
			print(" "*25,"*- %d generation -*"%self.generation)
			print("="*80)
			print("%-12s %-12s %-12s %-12s %-12s"%("Index","Fitness","XDecimal","MyTarget","RealTarget"))
			print("-"*80)
			myTargets = []
			for i in range(top):
				my_target = self.getTarget(xDecNums[i])
				myTargets.append(my_target)
				print("%-12d %-12.3f %-12.3f %-12.3f %-12.3f"%(i, fitnessList[i], xDecNums[i], my_target, self.target))
			print("-"*80)
			
			# Save
			self.XhistoricalTopPopulation.append(xDecNums[:top])
			self.TargethistoricalTopPopulation.append(myTargets.copy())
			
			if pause:
				sleep(0.5)
			
		else:
			
			xDecimal, fitness = self.getTop20Mean(xDecNums, fitnessList)
			my_target = self.getTarget(xDecimal)
			if self.generation == 1:
				print("%-12s %-12s %-12s %-12s %-12s"%("Generation","Fitness","XDecimal","MyTarget","RealTarget"))
				print("-"*100)
			print("%-12d %-12.3f %-12.3f %-12.3f %-12.3f"%(self.generation,fitness, xDecimal, my_target, self.target))
			
			# Save history
			self.historicalMeanFitness.append(fitness)
			self.historicalMeanXDecimal.append(xDecimal)
			self.historicalMeanTargetDecimal.append(my_target)
			
			if pause:
				sleep(0.5)
	
	def visualization(self):
		"""
		可視化世代曆史資訊
		"""
		
		# Fitness information about 
		plt.figure(figsize=(8,5))
		plt.subplot(2,1,1)
		plt.plot([i+1 for i in range(self.generation)],self.historicalMeanFitness,linestyle="--",marker="o")
		plt.ylabel("Fitness")
		
		# My Target information about 
		plt.subplot(2,1,2)
		plt.plot([i+1 for i in range(self.generation)],self.historicalMeanTargetDecimal,linestyle="--",marker="o")
		plt.ylabel("My Target real = %.3f"%self.target)
		
		plt.show()
	
	def visualizationDetail(self):
		"""
		可視化世代取值
		"""
		plt.ion()
		plt.figure(figsize=(8,5))
		plt.ion()
		xScale = [i for i in range(X_RANGE[0], X_RANGE[1])]
		yScale = getRange(self.xCoef, self.yCoef)
		
		for i in range(len(self.XhistoricalTopPopulation)):
			
			plt.cla()
			
			plt.plot(xScale,yScale, alpha=0.7, linestyle=":",label="Express Space",color="blue")
			
			plt.axhline(self.target,color="red",alpha=0.7,linestyle="--",label="Real Target Base Line")
			
			plt.scatter(
				self.XhistoricalTopPopulation[i],
				self.TargethistoricalTopPopulation[i],
				alpha=0.7,
				color="red",
				label = "My Target"
				)
			plt.title("Generation %d max is %d"%(i,self.maxGenerationSize))
			plt.ylim(Y_RANGE[0],Y_RANGE[1])
			plt.xlim(X_RANGE[0], X_RANGE[1])
			plt.legend()
			
			plt.pause(1)
		plt.close()
		
	def selectParent(self,size = 20):
		"""
		預設選擇适合度排名前20的父母,随機兩兩配對
		return index list of select species one is father another is mother 
		"""
		assert size < len(self.species), "selectParent Func size=%d par must less population%d"%(size, len(self.species))
		
		# get size of couple
		coupleSize = size // 2
		
		# get total index of couple
		total = set([i for i in range(coupleSize*2)])
		
		# father and mother 
		father = set(random.sample(total,coupleSize))
		mother = list(total - father)
		mother = random.sample(mother,coupleSize)
		father = random.sample(father,coupleSize)
		
		
		return father, mother
		
	
	def live(self):
		"""
		群體生存周期
		"""
		while self.generation < self.maxGenerationSize:
			self.nextGeneration()
	
	
	def nextGeneration(self):
		"""
		産生下一個世代
		"""
		# calculate current generation fitness
		fitnessList = self.calFitness(self.species)
		
		# order by fitness
		self.sortByFitness(fitnessList, self.species)
		
		#Survival individual
		# child born
		childs = self.getChildPopulation()

		#choose survival child as next generation
		fitnessListChild = self.calFitness(childs)
		self.sortByFitness(fitnessListChild, childs)
		
		#
		self.generation += 1
		
		#
		self.show(detail = DETAIL, pause = PAUSE)
		
		self.species = childs[:self.popSize]
		
	def getChildPopulation(self):
		"""
		子代出生
		return child
		"""
		# selectParent
		fathersI, mothersI = self.selectParent()
		L = len(fathersI)
		
		# get childs
		childs = []
		for i in range(L):
			for j in range(self.childsSize):
				# child born
				fI = fathersI[i]
				mI = mothersI[i]
				child = self.getChild(self.species[fI], self.species[mI])
				
				# add child to child population
				childs.append(child)
				
		return childs
	
	def getChild(self,f,m):
		"""
		1.二進制編碼交叉融合
		2.突變編碼
		單個孩子
		"""
		assert isinstance(f,NumberSpecies),"crossMerge(f,m) f and m must be NumberSpecies class"
		assert isinstance(m,NumberSpecies),"crossMerge(f,m) f and m must be NumberSpecies class"
		
		seed = random.uniform(0,1)
		
		# do crossMerge?
		# decide cross position
		childCode = []
		for i in range(f.totalSize):
			fromSeed = random.uniform(0,1)
			if fromSeed > self.crossMergeRate:
				childCode.append(f.code[i])
			else:
				childCode.append(m.code[i])
			
		
		# do mutate?
		# randomly choose x position to mutate
		if seed < self.mutateRate:
			tempPosIndex = [i for i in range(f.totalSize)]
			mutatePos = random.sample(tempPosIndex,self.mutatePosSize)
			
			# Change code
			for i in mutatePos:
				if childCode[i] == 0:
					childCode[i] = 1
					
				else:
					childCode[i] = 0
					
		# child born
		child = NumberSpecies(XINTSIZE,XDECSIZE)
		child.assignCode(childCode)

		return child

if __name__ == "__main__":
	# Define a express
	f="1000=12x**2+25x"
	
	# Individual code information
	XINTSIZE = 9
	XDECSIZE = 16
	
	# Population information
	POPULATION_SIZE = 100
	CHILD_SIZE = 10
	MAX_GENERATION_SIZE = 50
	MUTATE_SIZE = 5
	MUTATE_RATE = 0.05
	CROSS_MERGE_RATE = 0.5
	
	# visualization detail
	X_RANGE = [-100,100]
	Y_RANGE = [-500,6000]
	
	
	# Print detail
	DETAIL = False
	PAUSE = True
	
	# Create a population
	numPopulation = Population(
		popSize = POPULATION_SIZE,
		childsSize = CHILD_SIZE,
		maxGenerationSize = MAX_GENERATION_SIZE,
		formulate=f,
		)
	
	numPopulation.live()
	
	# visualization
	if not DETAIL:
		numPopulation.visualization()
	else:
		numPopulation.visualizationDetail()
		
	print("-"*80)
	best = numPopulation.species[0]
	bestX = best.getDecimal()
	bestTarget = numPopulation.getTarget(bestX)
	print("Best x = %.3f"%bestX)
	print("Best Target = %.3f"%bestTarget)
	print("%.3f = %.3fx**2 + %.3fx"%(numPopulation.target,numPopulation.xCoef,numPopulation.yCoef))
				
			
			
				
           

感謝各位閱讀,如有錯誤,敬請指正