遺傳算法的基本概念和實現(附 Java 實現案例)

2020-12-03 機器之心Pro

  基因遺傳算法是一種靈感源於達爾文自然進化理論的啟發式搜索算法。該算法反映了自然選擇的過程,即最適者被選定繁殖,並產生下一代。本文簡要地介紹了遺傳算法的基本概念和實現,希望能為讀者展示啟發式搜索的魅力。

 

  如上圖(左)所示,遺傳算法當個體由多條染色體組成,每條染色體由多個基因組成。上圖(右)展示了染色體分割和組合方式。

  自然選擇的概念

  自然選擇的過程從選擇群體中最適應環境的個體開始。後代繼承了父母的特性,並且這些特性將添加到下一代中。如果父母具有更好的適應性,那麼它們的後代將更易於存活。迭代地進行該自然選擇的過程,最終,我們將得到由最適應環境的個體組成的一代。

  這一概念可以被應用於搜索問題中。我們考慮一個問題的諸多解決方案,並從中搜尋出最佳方案。

  遺傳算法含以下五步:

  初始化

  個體評價(計算適應度函數)

  選擇運算

  交叉運算

  變異運算

  初始化

  該過程從種群的一組個體開始,且每一個體都是待解決問題的一個候選解。

  個體以一組參數(變量)為特徵,這些特徵被稱為基因,串聯這些基因就可以組成染色體(問題的解)。

  在遺傳算法中,單個個體的基因組以字符串的方式呈現,通常我們可以使用二進位(1和0的字符串)編碼,即一個二進位串代表一條染色體串。因此可以說我們將基因串或候選解的特徵編碼在染色體中。

  

  種群、染色體和基因

  個體評價利用適應度函數評估了該個體對環境的適應度(與其它個體競爭的能力)。每一個體都有適應度評分,個體被選中進行繁殖的可能性取決於其適應度評分。適應度函數值越大,解的質量就越高。適應度函數是遺傳算法進化的驅動力,也是進行自然選擇的唯一標準,它的設計應結合求解問題本身的要求而定。

  選擇運算

  選擇運算的目的是選出適應性最好的個體,並使它們將基因傳到下一代中。基於其適應度評分,我們選擇多對較優個體(父母)。適應度高的個體更易被選中繁殖,即將較優父母的基因傳遞到下一代。

  交叉運算

  交叉運算是遺傳算法中最重要的階段。對每一對配對的父母,基因都存在隨機選中的交叉點。

  舉個例子,下圖的交叉點為3。

  

  父母間在交叉點之前交換基因,從而產生了後代。

  父母間交換基因,然後產生的新後代被添加到種群中。

  

  變異運算

  在某些形成的新後代中,它們的某些基因可能受到低概率變異因子的作用。這意味著二進位位串中的某些位可能會翻轉。

 

  變異運算前後

  變異運算可用於保持種群內的多樣性,並防止過早收斂。

  終止

  在群體收斂的情況下(群體內不產生與前一代差異較大的後代)該算法終止。也就是說遺傳算法提供了一組問題的解。

案例實現

  種群的規模恆定。新一代形成時,適應度最差的個體凋亡,為後代留出空間。這些階段的序列被不斷重複,以產生優於先前的新一代。

  這一迭代過程的偽代碼:

  START

Generate the initial population

Compute fitness

REPEAT

Selection

Crossover

Mutation

Compute fitness

UNTIL population has converged

STOP

  Java中的示例實現

  以下展示的是遺傳算法在Java中的示例實現,我們可以隨意調試和修改這些代碼。給定一組五個基因,每一個基因可以保存一個二進位值0或1。這裡的適應度是基因組中1的數量。如果基因組內共有五個1,則該個體適應度達到最大值。如果基因組內沒有1,那麼個體的適應度達到最小值。該遺傳算法希望最大化適應度,並提供適應度達到最大的個體所組成的群體。注意:本例中,在交叉運算與突變運算之後,適應度最低的個體被新的,適應度最高的後代所替代。

  import java.util.Random;

/**

*

* @author Vijini

*/

//Main class

public class SimpleDemoGA {

Population population = new Population();

Individual fittest;

Individual secondFittest;

int generationCount = 0;

public static void main(String[] args) {

Random rn = new Random();

SimpleDemoGA demo = new SimpleDemoGA();

//Initialize population

demo.population.initializePopulation(10);

//Calculate fitness of each individual

demo.population.calculateFitness();

System.out.println("Generation: " + demo.generationCount + " Fittest: " + demo.population.fittest);

//While population gets an individual with maximum fitness

while (demo.population.fittest < 5) {

++demo.generationCount;

//Do selection

demo.selection();

//Do crossover

demo.crossover();

//Do mutation under a random probability

if (rn.nextInt()%7 < 5) {

demo.mutation();

}

//Add fittest offspring to population

demo.addFittestOffspring();

//Calculate new fitness value

demo.population.calculateFitness();

System.out.println("Generation: " + demo.generationCount + " Fittest: " + demo.population.fittest);

}

System.out.println("\nSolution found in generation " + demo.generationCount);

System.out.println("Fitness: "+demo.population.getFittest().fitness);

System.out.print("Genes: ");

for (int i = 0; i < 5; i++) {

System.out.print(demo.population.getFittest().genes[i]);

System.out.println("");

}

//Selection

void selection() {

//Select the most fittest individual

fittest = population.getFittest();

//Select the second most fittest individual

secondFittest = population.getSecondFittest();

//Crossover

void crossover() {

//Select a random crossover point

int crossOverPoint = rn.nextInt(population.individuals[0].geneLength);

//Swap values among parents

for (int i = 0; i < crossOverPoint; i++) {

int temp = fittest.genes[i];

fittest.genes[i] = secondFittest.genes[i];

secondFittest.genes[i] = temp;

//Mutation

void mutation() {

//Select a random mutation point

int mutationPoint = rn.nextInt(population.individuals[0].geneLength);

//Flip values at the mutation point

if (fittest.genes[mutationPoint] == 0) {

fittest.genes[mutationPoint] = 1;

} else {

mutationPoint = rn.nextInt(population.individuals[0].geneLength);

if (secondFittest.genes[mutationPoint] == 0) {

secondFittest.genes[mutationPoint] = 1;

//Get fittest offspring

Individual getFittestOffspring() {

if (fittest.fitness > secondFittest.fitness) {

return fittest;

return secondFittest;

//Replace least fittest individual from most fittest offspring

void addFittestOffspring() {

//Update fitness values of offspring

fittest.calcFitness();

secondFittest.calcFitness();

//Get index of least fit individual

int leastFittestIndex = population.getLeastFittestIndex();

//Replace least fittest individual from most fittest offspring

population.individuals[leastFittestIndex] = getFittestOffspring();

}

//Individual class

class Individual {

int fitness = 0;

int[] genes = new int[5];

int geneLength = 5;

public Individual() {

//Set genes randomly for each individual

for (int i = 0; i < genes.length; i++) {

genes[i] = rn.nextInt() % 2;

fitness = 0;

//Calculate fitness

public void calcFitness() {

if (genes[i] == 1) {

++fitness;

}

//Population class

class Population {

int popSize = 10;

Individual[] individuals = new Individual[10];

int fittest = 0;

//Initialize population

public void initializePopulation(int size) {

for (int i = 0; i < individuals.length; i++) {

individuals[i] = new Individual();

//Get the fittest individual

public Individual getFittest() {

int maxFit = Integer.MIN_VALUE;

if (maxFit <= individuals[i].fitness) {

maxFit = i;

fittest = individuals[maxFit].fitness;

return individuals[maxFit];

//Get the second most fittest individual

public Individual getSecondFittest() {

int maxFit1 = 0;

int maxFit2 = 0;

if (individuals[i].fitness > individuals[maxFit1].fitness) {

maxFit2 = maxFit1;

} else if (individuals[i].fitness > individuals[maxFit2].fitness) {

//Get index of least fittest individual

public int getLeastFittestIndex() {

int minFit = 0;

if (minFit >= individuals[i].fitness) {

minFit = i;

return minFit;

//Calculate fitness of each individual

public void calculateFitness() {

individuals[i].calcFitness();

getFittest();

}

相關焦點

  • 一文讀懂遺傳算法工作原理(附Python實現)
    原標題:一文讀懂遺傳算法工作原理(附Python實現) 選自AnalyticsVidhya 參與:晏奇、黃小天 你也許在想:這句話和遺傳算法有什麼關係?其實遺傳算法的整個概念就基於這句話。
  • 面試官:給我手寫一個哈夫曼編碼(java語言實現)
    一、基本概念哈夫曼樹的目的是找出存放一串字符所需的最少的二進位編碼, 原理是通過統計出每種字符出現的頻率!不斷地對其合併。舉個例子:有一串字符,現在把這些字符進行統計,頻率表 A:60, B:45, C:13 D:69 E:14 F:5 G:3。現在要對這些字符進行編碼,但是前提是使用最少的二進位編碼。這時候怎麼辦呢?
  • 用經典案例來幫助初學者解析Java的「多態」
    我這裡還是用我13年前給我們公司新員工做內部培訓時用到的看起來似乎有點老掉牙的、但是仍然十分經典的案例來重新給有需要的java愛好者呈現一下「多態」的奧秘所在!概念:Java中這種相同類型的對象(或說是「變量」)、調用了相同的方法、執行的具體代碼卻不同、運行的結果也不同的現象,我們稱之為「多態」!這裡理論上的東西咱就先往後放一放,咱們先看看案例中的具體代碼、品一品、悟一悟、回味回味,可能就已經透徹了很多!
  • 資料|《常用數據挖掘算法總結及 Python 實現》
    今日資料推薦《 常用數據挖掘算法總結及 Python 實現 》這份資源非常適合相關的從業人員或大數據愛好者,該文檔總結了常用的數據挖掘的算法原理以及 Python 實踐內容,為初學者提供良好的參考資料第七部分:SQL 知識第八部分:數據挖掘案列分析案例一 A Journey through Titanic 597c770e案例二 Analysis for airplane-crashes-since
  • 變壓器之遺傳算法(Genetic Algorithm)的具體實現過程
    遺傳算法(Genetic Algorithm)是一類借鑑生物界的進化規律(適者生存,優勝劣汰遺傳機制)演化而來的隨機化搜索方法。
  • Java類隔離加載實現原理是什麼?
    只要Java 代碼寫得足夠多就一定會出現這種情況:系統新引入了一個中間件的 jar 包,編譯的時候一切正常,一運行就報錯:java.lang.NoSuchMethodError,然後就哼哧哼哧的開始找解決方法,最後在幾百個依賴包裡面找的眼睛都快瞎了才找到衝突的 jar,把問題解決之後就開始吐槽中間件為啥搞那麼多不同版本的 jar,寫代碼五分鐘,排包排了一整天。
  • Java基礎學習:java中的基本數據類型
    2、包裝器類型 基本數據類型不符合面向對象思想,從而出現了包裝器類型,並且包裝器添加了更多的屬性和方法,自動包裝功能可以將基本類型轉換為包裝器類型。Java為每個原始類型都提供了一個封裝類,Integer、Double、Long、Boolean、Byte等等。
  • Java面向對象之final、abstract抽象、和變量生命周期
    出於安全考慮,類的實現細節不允許被拓展和修改。比如:基本數據類型的包裝類就是一個典型的例子。該類不會再被拓展。java裡final修飾的類有很多,比如八大基本數據類型的包裝類(Byte,Character、Short、Integer、Long、Float、Double、Boolean)和String等。
  • 三張圖讀懂機器學習:基本概念、五大流派與九種常見算法
    四大會計師事務所之一的普華永道(PwC)近日發布了多份解讀機器學習基礎的圖表,其中介紹了機器學習的基本概念、原理、歷史、未來趨勢和一些常見的算法。為便於讀者閱讀,機器之心對這些圖表進行了編譯和拆分,分三大部分對這些內容進行了呈現,其中也加入了一些擴展連結,希望能幫助你進一步擴展閱讀。 一、機器學習概覽
  • 用Java實現目標檢測|PyTorch
    可是這兩種解決方案都沒有辦法能讓Java開發者很好的使用:用戶需要從易於使用和易於維護中二選一。針對於這個問題,亞馬遜雲服務 (AWS)開源了 Deep Java Library (DJL),一個為Java開發者設計的深度學習庫。它兼顧了易用性和可維護性,一切運行效率以及內存管理問題都得到了很好的處理。DJL使用起來異常簡單。
  • 詳解遺傳算法
    遺傳算法是受達爾文的進化論的啟發,借鑑生物進化過程而提出的一種啟發式搜索算法。因此在介紹遺傳算法前有必要簡單的介紹生物進化知識。比例選擇實現算法就是所謂的「輪盤賭算法」( Roulette Wheel Selection ) ,輪盤賭算法的一個簡單的實現如下:輪盤賭算法 /** 按設定的概率,隨機選中一個個體* P[i]表示第i個個體被選中的概率*/int RWS(){
  • 用Java實現目標檢測 | PyTorch
    很多論文都選擇使用PyTorch去實現也證明了它在訓練方面的效率以及易用性。在PyTorch領域,儘管部署一個模型有很多選擇,可為Java開發人員準備的選項卻屈指可數。在過去,用戶可以用PyTorch C++ 寫JNI (Java Native Interface) 來實現這個過程。
  • Python零基礎入門—算法的實現與偽代碼
    在前面一節課,我們了解了什麼是算法,知道了在一個算法中,要有輸入、計算過程、還要有輸出。這節課我們來討論算法的實現。這節課的內容與前面課程的課後練習有關。在課後練習中要求同學們寫出計算長方形面積算法的步驟,步驟要包含輸入、計算過程和輸出。老師在這裡寫出計算長方形面積算法的步驟,同學們可以和自己寫的算法步驟比較一下,看看哪個寫的更詳細和完善一些?
  • 遺傳算法原理以及在量化投資的應用
    原標題:遺傳算法原理以及在量化投資的應用 點擊標題下「藍色微信名」可快速關注 本篇內容涉及遺傳算法的概念,原理描述,實現方法以及在量化投資的應用。 陳煥生,凡普金科旗下會牛科技研發總監兼數據架構師,目前從事基於遺傳算法因子自動化挖掘,量化投資研究。並於2017年上線了基於遺傳算法因子挖掘的自有資金運營的量化模型。目前處於行業中遊水平,團隊的大多背景都是非金融投資領域,實現網際網路技術向量化投資領域的轉型,本人十年的網際網路研發背景,多次連續創業的經歷。
  • 設計模式之策略模式(Java實現例子說明)
    有了這個例子,我相信你應該對其思想有了一個基本的認識,下面看一下其正式的概念介紹:定義一系列的算法,把每一個算法封裝起來, 並且使它們可相互替換二、實現策略模式策略模式把對象本身和運算規則區分開來,因此我們整個模式也分為三個部分。環境類(Context):用來操作策略的上下文環境,也就是我們遊客。
  • AI系統首次實現真正自主編程:利用遺傳算法,完爆初級程式設計師
    現在,來自彭博和英特爾實驗室的兩位研究人員,號稱實現了首個能夠自動生成完整軟體程序的AI系統「AI Programmer」,這個「AI程式設計師」利用遺傳算法和圖靈完備語言,開發的程序理論上能夠完成任何類型的任務。AI自動編程的時代,大幕已開。 讓AI自動編程一直是計算機科學家的夢想。目前這個方面的成果還非常有限,比如讓AI自動補完程式語言,或者執行簡單的加法程序。
  • 什麼是遺傳算法?怎樣繪製遺傳算法流程圖
    遺傳算法是一種通過模擬自然進化過程搜索最優解的操作方法,遺傳算法有三個基本算子選擇、交叉和變異。對於遺傳算法我們也可以使用流程圖對其整個過程進行總結歸納,那要怎樣繪製遺傳算法流程圖呢?下面是分享的簡單操作方法,希望可以幫助大家。
  • 面試頻率最高的簡單問題——Java類的三大基本特徵
    學習過Java的程式設計師都知道,java類有三大特徵——封裝、繼承和多態。下面的文章給大家詳細的介紹一下java的這三大特性。封裝封裝是將描述某類事物的數據與處理這些數據的函數封裝在一起,形成一個有機整體,稱為類。類所具有的的封裝性可使程序模塊具有良好的獨立性與可維護性。
  • Java API + Python AI,實現跨平臺任務調度
    1,Java + Spring Boot開發Web服務是常用搭配,豐富的組件和易用的功能;2,Python在AI領域是主流開發語言,實現業務處理更方便,不需要代碼移植;3,招聘工程師組建技術團隊有針對性,發揮各自優勢。
  • 教程| 基礎入門:深度學習矩陣運算的概念和代碼實現
    本文由機器之心編輯,「機器之心」專注生產人工智慧專業性內容,適合開發者和從業者閱讀參考。點擊右上角即刻關注。本文從向量的概念與運算擴展到矩陣運算的概念與代碼實現,對機器學習或者是深度學習的入門者提供最基礎,也是最實用的教程指導,為以後的機器學習模型開發打下基礎。