遺傳演算法(Genetic Algorithm )+C++實現解決TSP問題
概念
生物進化中的概念 | 遺傳演算法中的作用 |
---|---|
環境 | 適應函式 |
適應性 | 適應函式值 |
適者生存 | 適應值大的解被保留的概率大 |
個體 | 問題的一個解 |
染色體 | 解的編碼 |
基因 | 編碼的元素 |
群體 | 被選定的一組解 |
種群 | 按適應函式選擇的一組解(編碼表示) |
交配 | 以一定的方式由雙親產生後代的過程 |
變異 | 編碼的某些分量發生變化的過程 |
三個生成過程
- select 自然選擇
- crossover 交叉(交配)
- mutation 變異
把每個資料想象成染色體,然後從染色體到人的對映就是object function,也就是這裡的適應函式。
當然可以對映到更深的程度,比如考慮人的身高之類的(可以被量化的東西)
- 染色體 這樣的比喻,會很容易理解crossover這個步驟。
- 變異這個也很好理解,避免進入到區域性極小值,被控制住了。
- 自然選擇,這個根據evolutionary theory,也很容易理解
簡單遺傳演算法框架
一般終止條件是:過了很久最優的適應值都不發生變化
整數編碼問題
因為如果是二進位制編碼的話,會簡單很多,這裡就不講了。
難點其實還是在整數編碼上。
整數編碼(簡單的例項):
(有些問題不是排序的問題,就可以類似於之前的二進位制來實現)
對於父母分別是 0 到 9整數的排序【加上長度均為10】:
要求子代也必須是這樣的排序。
- 自然選擇:不會產生子代,只是篩選子代,所以不受這個問題影響
- 交叉(crossover):會產生子代。這裡只考慮排序時候的情況
- 基於次序的交配法:
- 基於位置的交配法: 在父代1找到幾個位置,父代2的數值直接替代上去,只會,衝突的位置(不在之前選的位置上衝突的位置),按順序從父代1替代。
- 部分對映的交配法: 任意選兩個位置,在父代1,2直接這兩個位置之間的序列構建序列對。然後,按照這樣的序列對的對映方式,在父代1或者父代2上做對映交換。就可以得到子代1或子代2。
- 基於次序的交配法:
- 變異(mutation):會產生子代。
- 基於位置的變異: 隨機產生兩個變異位,然後將第二個變異位上的基因移動到第一個變異位之前。
- 基於次序的變異: 隨機的產生兩個變異位,然後交換這兩個變異位上的基因。
- 打亂變異: 隨機尋去染色體上的一段,然後打亂在該段內的基因次序。逆序交換方式是打亂變異的一個特例。
TSP問題
TSP,是貨郎擔問題,也就是中國郵遞員問題(少數世界級問問題,用中國人命名的問題hhh)。
就是n個點直接連通需要不同的代價,如果想要找到不重複的經歷完所有點,然後在回到初始點的用的代價最小。
用遺傳演算法解決TSP問題
#include <iostream>
#include <cmath>
#include <ctime>
#include <fstream>
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;
// 隨機整數[b, e)
#define RAND(b, e) (rand() % ((e)-(b)) + (b))
// 隨機浮點數 0,1
#define RANDFLOAT() ((double)rand() / double(RAND_MAX))
// 種群數量
int LEN = 10;
int tempLEN = 3 * LEN;
// 交配的概率,編譯的概率
double pc = 0.8, pm = 0.8;
// 種群的所有路徑
int **Paths;
int **tempPaths;
int temp_index = 0;
double **Mat;
double *Value, *tempValue;
int globalValue_index, stay_in_global_times = 0;
double globalValue;
int *globalPath;
// TSP節點數量
int N = 0;
// 適應值函式
double CalValue(int *p) { // read only
double t = 0;
for (int i = 1; i < N; ++i) {
t += Mat[p[i - 1]][p[i]];
}
t += Mat[p[N - 1]][0];
return t;
}
void initialPath(int *Path, int j);
void initialPaths();
void find_min(int first = 1);
// 自然選擇
void select();
// 交配
void crossover(int *p1, int* p2);
// 變異
void mutation(int *Path);
//
void preserve(int *p1, int *p2, int v1, int v2);
int main() {
srand((unsigned)time(NULL));
ifstream cin("data.txt");
cin >> N;
Mat = new double *[N];
for (int i = 0; i < N; ++i) {
Mat[i] = new double[N];
}
// read data from data.txt
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
cin >> Mat[i][j];
}
}
// new Path...
Paths = new int*[LEN];
for (int i = 0; i < LEN; ++i) {
Paths[i] = new int[N];
}
tempPaths = new int*[tempLEN];
for (int i = 0; i < tempLEN; ++i) {
tempPaths[i] = new int[N];
}
Value = new double[LEN];
tempValue = new double[tempLEN];
globalPath = new int[N];
// end new Path...
initialPaths(); // initialize paths
while (true) {
temp_index = 0;
for (int i = 0; i < LEN; i += 2) {
if (temp_index >= tempLEN) break;
preserve(Paths[i], Paths[i + 1], Value[i], Value[i + 1]);
if (temp_index >= tempLEN) break;
if (RANDFLOAT() < pc) crossover(Paths[i], Paths[i + 1]);
}
for (int i = 0; i < LEN; ++i) {
if (temp_index >= tempLEN) break;
if (RANDFLOAT() < pm) mutation(Paths[i]);
}
select();
if (stay_in_global_times == 1000) { break; }
}
cout << globalValue << endl;
for (int i = 0; i < N; ++i) {
cout << globalPath[i] << " --> ";
}
// delete Path...
delete[]tempValue;
delete[]Value;
for (int i = 0; i < tempLEN; ++i) {
delete[]tempPaths[i];
}
delete[] tempPaths;
for (int i = 0; i < LEN; ++i) {
delete[]Paths[i];
}
delete[] Paths;
for (int i = 0; i < N; ++i) {
delete[] Mat[i];
}
delete[]Mat;
system("pause");
}
// preserve the parents.
void preserve(int *p1, int *p2, int v1, int v2) {
for (int i = 0; i < N; ++i) {
tempPaths[temp_index][i] = p1[i];
}
tempValue[temp_index] = v1;
//if (tempValue[temp_index] < 0) cout << "wrong\n";
temp_index++;
if (p2 != NULL) {
for (int i = 0; i < N; ++i) {
tempPaths[temp_index][i] = p2[i];
}
tempValue[temp_index] = v2;
//if (tempValue[temp_index] < 0) cout << "wrong\n";
temp_index++;
}
}
struct enumate{
double data;
int index;
bool operator < (const enumate& e) const {
return data < e.data;
}
};
vector<int> argsort_temp_value() {
enumate * data = new enumate[temp_index];
for (int i = 0; i < temp_index; ++i) {
data[i].data = tempValue[i];
data[i].index = i;
}
sort(data, data +temp_index);
vector<int> ans(temp_index);
for (int i = 0; i < temp_index; ++i) ans[i] = data[i].index;
delete[]data;
return ans;
}
void select() {
vector<int> id = argsort_temp_value();
for (int i = 0; i < temp_index; ++i) if (tempValue[i] < 0)cout << tempValue[i] << endl;
for (int i = 0; i < LEN; ++i) {
Value[i] = tempValue[id[i]];
for (int j = 0; j < N; ++j) {
Paths[i][j] = tempPaths[id[i]][j];
}
}
if (Value[0] < globalValue) {
for (int i = 0; i < N; ++i) globalPath[i] = Paths[0][i];
stay_in_global_times = 0;
globalValue = Value[0];
}
else if (Value[0] == globalValue) {
stay_in_global_times++;
}
else {
cout << "Something wrong" << Value[0]<< endl;
}
}
// 基於次序的交配方式
void crossover(int * p1, int * p2)
{
// generate son from p1 and p2
int crossn = RAND(0, N-1);
if (crossn == 0) return;
int * indexs = new int[crossn];
for (int i = 0; i < crossn; ++i) {
if (i == 0) indexs[i] = RAND(1, N - crossn + 1);
else {
indexs[i] = RAND(indexs[i - 1] + 1, N - crossn + i + 1);
}
}
// copy from p2
for (int i = 0; i < N; ++i) {
tempPaths[temp_index][i] = p2[i];
}
int use_count = 0;
for (int i = 0; i < N; ++i) {
if (use_count == crossn) break;
for (int j = 0; j < crossn; ++j) {
if (tempPaths[temp_index][i] == p1[indexs[j]]) {
tempPaths[temp_index][i] = p1[indexs[use_count]];
use_count++;
break;
}
}
}
// cal value
tempValue[temp_index] = CalValue(tempPaths[temp_index]);
temp_index++;
// copy from p1
for (int i = 0; i < N; ++i) {
tempPaths[temp_index][i] = p1[i];
}
use_count = 0;
for (int i = 0; i < N; ++i) {
if (use_count == crossn) break;
for (int j = 0; j < crossn; ++j) {
if (tempPaths[temp_index][i] == p2[indexs[j]]) {
tempPaths[temp_index][i] = p2[indexs[use_count]];
use_count++;
break;
}
}
}
tempValue[temp_index] = CalValue(tempPaths[temp_index]);
temp_index++;
delete[] indexs;
}
// 基於次序的變異
void mutation(int * Path)
{
int a = RAND(1, N), b, t;
if (a == N - 1) {
b = RAND(1, N - 1);
t = a;
a = b;
b = t;
}
else {
b = RAND(a + 1, N);
}
for (int i = 0; i < N; ++i) {
if (i == a)
tempPaths[temp_index][i] = Path[b];
else if ( i == b )
tempPaths[temp_index][i] = Path[a];
else tempPaths[temp_index][i] = Path[i];
}
tempValue[temp_index] = CalValue(tempPaths[temp_index]);
temp_index++;
}
void initialPaths() {
for (int i = 0; i < LEN; ++i)
initialPath(Paths[i], i);
find_min();
}
void find_min(int first)
{
int temp = 0;
for (int i = 1; i < LEN; ++i)
if (Value[temp] > Value[i]) temp = i;
if (first) {
globalValue = Value[temp];
for (int i = 0; i < N; ++i)
globalPath[i] = Paths[temp][i];
}
else if (Value[temp] < globalValue) {
globalValue = Value[temp];
for (int i = 0; i < N; ++i)
globalPath[i] = Paths[temp][i];
}
}
void initialPath(int *Path, int j) {
for (int i = 0; i < N; ++i) {
Path[i] = i;
}
// Path[i]表示路上第i個點的標記為Path[i]
// Path[0] = 0
int tx, t;
for (int i = 1; i < N - 1; ++i) {
tx = RAND(i, N);
if (tx != i) { // swap
t = Path[i];
Path[i] = Path[tx];
Path[tx] = t;
}
相關推薦
遺傳演算法(Genetic Algorithm )+C++實現解決TSP問題
概念
生物進化中的概念
遺傳演算法中的作用
環境
適應函式
適應性
適應函式值
適者生存
適應值大的解被保留
【尋優演算法】遺傳演算法(Genetic Algorithm) 引數尋優的python實現
【尋優演算法】遺傳演算法(Genetic Algorithm) 引數尋優的python實現
一、遺傳演算法簡介
1、遺傳演算法由來
2、遺傳演算法名詞概念
3、遺傳演算法中對染色體的操作
3.1、選擇
3.2
簡單的遺傳演算法(Genetic algorithms)-吃豆人
遺傳演算法簡介:
一直都在收聽卓老闆聊科技這個節目,最近播出了一起人工智慧的節目,主要講的是由霍蘭提出的遺傳演算法,在目中詳細闡述了一個有趣的小實驗:吃豆人。
首先簡單介紹下遺傳演算法:
1:為了解決某個具體的問題,先隨機生成若干個解決問題的實體,每個實體
分水嶺演算法(Watershed algorithm)與OpenCV實現
前言
分水嶺演算法主要用於影象的分割!
這個演算法需要輸入一個灰度圖,在接下來的洪水漫堤過程中,相鄰的積水盆地之間的分水嶺便慢慢構建起來。一般情況下,這會引起過分割,尤其是具有噪聲的影象。
影象必須要預處理,以消除噪聲;分割結果必須要基於
Python實現遺傳演算法(二進位制編碼)求函式最優值
目標函式
maxf(x1,x2)=21.5+x1sin(4πx1)+x2sin(20πx2)
max f({x_1},{x_2}) = 21.5 + {x_1}\sin (4\pi {x_1}) + {x_2}\sin (20\pi {x_2})
1015 Reversible Primes (20 分)C++實現 (已AC)
題目
題目連結:https://pintia.cn/problem-sets/994805342720868352/problems/994805495863296000
思路:
這道題的思路很簡單, 檢查輸入數是否是質數, 如果是的話, 把它按進位制轉換, 再翻轉, 轉換回
1014 Waiting in Line (30 分)C++實現(已AC)
題目
題目連結:https://pintia.cn/problem-sets/994805342720868352/problems/994805498207911936
思路:
建立一個視窗列表,每個列表維護一個M長度的佇列, 以及佇列末尾的人服務結束的時間, 逐個插入客戶
1012 The Best Rank (25 分)c++實現(已AC)
題目:
連結:https://pintia.cn/problem-sets/994805342720868352/problems/994805502658068480
思路:
像是自己見一個數據庫, 用以增和查, 我的思路是按照4個科目(M, C, E, A)建四個表, 每
1010 Radix (25 分)C++實現-終於AC了
題目
題目連結:https://pintia.cn/problem-sets/994805342720868352/problems/994805507225665536 是一道目前為止比較有意思的題,也是碰到的為數不多需要考慮上溢位的題目
知識點:整型上溢位, 二分查詢
思
1004 Counting Leaves (30 分)c++實現
題目
https://pintia.cn/problem-sets/994805342720868352/problems/994805521431773184
解題思路與程式碼實現
思路1:
構建一個結構體,包含三個元素,id,是否有孩子, 層級 構建一個數組維護樹結構
堆排序 (heap sort) C# 實現
main 函式中呼叫:
var data = new int[] {5,7,6,4,1,9,3}; heapSort(data);
【深度學習筆記】優化演算法( Optimization Algorithm)
本文依舊是吳恩達《深度學習工程師》課程的筆記整理與拓展。
一、優化演算法的目的與挑戰
優化演算法主要是用來加快神經網路的訓練速度,使得目標函式快速收斂。
優化問題面臨的挑戰有病態解、鞍點、梯度爆炸與梯度消失……具體可見參考文獻【1】241頁到249頁。
1152 Google Recruitment (20 分)(c++實現 已AC)
思路
題目的大意很簡單, 在L長度的數字字串中找到第一個k長度的素數. 如果找不到哦啊返回404, 找到了返回這個k長度的字串. 一個簡單暴力的思路就是遍歷L長度, 檢查從這個字元起往後k長度的字串表示的數字是否是素數.
測試點:
// 樣例測試點
20 5
23654987
1043 Is It a Binary Search Tree (25 分)C++實現(已AC)
題目
題目連結 :https://pintia.cn/problem-sets/994805342720868352/problems/994805440976633856
思路:
建樹的思路是不可行的, 對於普通二叉樹一定需要中序遍歷和另一個遍歷來複原,對於二叉搜尋樹來說,
1026 Table Tennis (30 分)C++實現(已AC)
題目:
題目連結:https://pintia.cn/problem-sets/994805342720868352/problems
思路:
感覺我的思路比較清奇, 導致程式碼寫的比較長… 我的思路是: 建立一個客戶列表,時間按照秒儲存,按照到達時間排序; 桌子放入一個列表
排序演算法(sorting algorithm)之 插入排序(insertion sort)
https://en.wikipedia.org/wiki/Insertion_sort
loop1:
4,6,1,3,7 -> 4,6,1,3,7
loop2:
4,6,1,3,7 -> 4,1,6,3,7
&nb
線性判別--感知機演算法(perceptron algorithm)
感知器演算法是一種線性判別演算法,它適用於二分類模型。在這個模型中,輸入向量x\mathbf{x}x首先使用一個固定的非線性變換得到一個特徵向量ϕ(x)\phi(\mathbf{x})ϕ(x),接著用這個特徵向量構造一個線性模型:
(1)y(x)=f(wTϕ
馬拉車演算法(Manacher Algorithm)--用於計算最長迴文子串
馬拉車演算法的目標是找到一串字串中的最長迴文子串,優點是時間複雜度為O(n) 現以尋找 “cgbaabgk” 中的最長子迴文串( “gbaabg”)為例進行說明
演算法過程(總共3步):
1.改造字串結構:
字元座標
0
1
合併排序(Merge Sort)C 實現(簡單效能測試)
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#define sential RAND_MAX /* 定義哨兵*/
#define SIZE 1000000/
迪傑斯特拉演算法(Dijkstra algorithm)
emmmm....寫語氣詞被同桌吐槽啊....嫌我emmm太長。桑心QAQ
好把同桌趕跑了~
這次來講迪傑斯特拉,這個東西嘛...和我們上一次看的弗洛伊德差不多啦,對沒錯不是那個寫性學三論的傢伙,所以不用期待我的文章裡會出現什麼奇