遗传算法解决八数码有关问题
遗传算法解决八数码问题
个人理解:
达尔文的进化论,主要是指自然界的优胜劣汰,自然对生物的选择,生物对自然界的适应而得到进化。
孟德尔的遗传学说,主要指生物的进化主要是通过染色体之间的交叉和变异来完成的。
而遗传算法中,
通过编码,将问题问题的解表示成种群的个体(染色体),适应度函数就是对种群中个体的选择的条件,也是种群中个体进化的方向。
通过适应度函数和选择算子,实现对生物的选择
通过交叉算子和变异算子,实现生物的进化
则:(1)遗传算法是一种随机算法,
(2)产生近似最优解,(不一定是最优解)
(3)通过编码表示具体的问题的解,通常采用2进制编码,对个体(染色体)解码后可得到一组可行解
(4)随机生成候选解,编码后产生初代种群。
(4)基于具体问题的适应度函数相当于自然界的对生物选择条件,通常适应度函数和解的优劣成正比。
(5
选择算子:选择算子通过适应度函数得到的种群中个体的适应度,实现对候选解的的选择,选择较优的解,淘汰较差的解。
选择出的个体参加,再按照一定的概率发生交叉运算、变异运算,遗传到下一代,或直接遗传到下一代。
轮盘赌选择法 (roulette wheel selection)是最简单也是最常用的选择方法。在该方法中,各个个体的选择概率和其适应度值成比例。
交叉算子:实现两个个体的部分基因结构加以替换重组而生成新个体的。单点交叉,两点交叉等。
变异算子:基本内容是对群体中的个体串的某些基因座上的基因值作变动。改变某个,或某连续几个基因组
(6)精英遗传策略:即适应度高的个体,之间遗传到下一代
具体到八数码问题中:
(1)采用二进制的编码:0的位置共有4中运算,则共有00, 01, 10, 11, 4种可能2位编码即可
(2)适应度函数:
1)跳过0,{100 - i * 10}求和i属于(1, 8),终止状态的值为440;
2)跳过0,所有未就位的的数字到就位的曼哈顿距离的和。
(3)选择算子:一种改进的轮盘赌选择法
(4)交叉算子:两点交叉法,将两个染色体的某个区间交换
(5)编译算子:随机选择某连续几位,将各位取反
(6)精英遗传策略:直接将适应度高的个体遗传到下一代
(7)初代种群,通过随机产生
(8)所有的随机数都是通过rand()产生的,并设置srand((unsigned long)time(0))
(9)题目的输入、输出和poj1077 Eight的输入输出相同,同时输出最后迭代的代数,代数为最大遗传代数,表示没有在最大遗传代数中找到解
(10)变量参数的选择:
群体规模:PS = 150;
基因规模:GS = 140;
交叉概率:PC = 0.75;
交叉最大长度:CMaxLen = 20;
变异概率:PM = 0.09;
变异最大长度:MMaxLen = 10;
最大遗传代数:DEEP = 1000;
适应度最优值: SussessValue = 440;
精英遗传规模:DS = 10;
(11)遗传算法有很多需要优化的地方,我写的这个的效率不是很高,收敛的效果并不是很好,这里只是对遗传算法的思想进行了学习。
遗传算法的八数码代码:
参考:百度百科,wiki,
论文《基于遗传算法的八数码问题的设计及实现》,论文《选择算子和遗传算法的计算效率分析》,论文:《改进的遗传算子选择算法》
http://blog.****.net/b2b160/article/details/4680853
http://www.cnblogs.com/heaad/archive/2010/12/23/1914725.html
http://blog.****.net/sld009/article/details/7046550
下面摘自wiki:
遗传算法是模仿自然界生物进化机制发展起来的随机全局搜索和优化方法,它借鉴了 达尔文的进化论和孟德尔的遗传学说。其本质是一种高效、并行、全局搜索的方法,它能在搜索过程中自动获取和积累有关搜索空间的知识,并自适应的控制搜索过程以求得最优解。遗传算法操作使用适者生存的原则,在潜在的解决方案种群中逐次产生一个近似最优解的方案,在遗传算法的每一代中,根据个体在问题域中的适应度值和从自然遗传学中借鉴来的再造方法进行个体选择,产生一个新的近似解。这个过程导致种群中个体的进化,得到的新个体比原来个体更能适应环境,就像自然界中的改造一样。
基于对自然界中生物遗传与进化机理的模仿,针对不同的问题,很多学者设计了许多不同的编码方法来表示问题的可行解,开发出了许多种不同的遗传算子来模仿不同环境下的生物遗传特性。
这样,由不同的编码方法和不同的遗传算子就构成了各种不同的遗传算法。
下面摘自百度百科:
遗传算法的基本运算过程如下:
a)初始化:设置进化代数计数器t=0,设置最大进化代数T,随机生成M个个体作为初始群体P(0)。
b)个体评价:计算群体P(t)中各个个体的适应度。
c)选择运算:将选择算子作用于群体。选择的目的是把优化的个体直接遗传到下一代或通过配对交叉产生新的个体再遗传到下一代。选择操作是
立在群体中个体的适应度评估基础上的。
d)交叉运算:将交叉算子作用于群体。遗传算法中起核心作用的就是交叉算子。
e)变异运算:将变异算子作用于群体。即是对群体中的个体串的某些基因座上的基因值作变动。
群体P(t)经过选择、交叉、变异运算之后得到下一代群体P(t+1)
f)终止条件判断:若t=T,则以进化过程中所得到的具有最大适应度个体作为最优解输出,终止计算。
个人理解:
达尔文的进化论,主要是指自然界的优胜劣汰,自然对生物的选择,生物对自然界的适应而得到进化。
孟德尔的遗传学说,主要指生物的进化主要是通过染色体之间的交叉和变异来完成的。
而遗传算法中,
通过编码,将问题问题的解表示成种群的个体(染色体),适应度函数就是对种群中个体的选择的条件,也是种群中个体进化的方向。
通过适应度函数和选择算子,实现对生物的选择
通过交叉算子和变异算子,实现生物的进化
则:(1)遗传算法是一种随机算法,
(2)产生近似最优解,(不一定是最优解)
(3)通过编码表示具体的问题的解,通常采用2进制编码,对个体(染色体)解码后可得到一组可行解
(4)随机生成候选解,编码后产生初代种群。
(4)基于具体问题的适应度函数相当于自然界的对生物选择条件,通常适应度函数和解的优劣成正比。
(5
选择算子:选择算子通过适应度函数得到的种群中个体的适应度,实现对候选解的的选择,选择较优的解,淘汰较差的解。
选择出的个体参加,再按照一定的概率发生交叉运算、变异运算,遗传到下一代,或直接遗传到下一代。
轮盘赌选择法 (roulette wheel selection)是最简单也是最常用的选择方法。在该方法中,各个个体的选择概率和其适应度值成比例。
交叉算子:实现两个个体的部分基因结构加以替换重组而生成新个体的。单点交叉,两点交叉等。
变异算子:基本内容是对群体中的个体串的某些基因座上的基因值作变动。改变某个,或某连续几个基因组
(6)精英遗传策略:即适应度高的个体,之间遗传到下一代
具体到八数码问题中:
(1)采用二进制的编码:0的位置共有4中运算,则共有00, 01, 10, 11, 4种可能2位编码即可
(2)适应度函数:
1)跳过0,{100 - i * 10}求和i属于(1, 8),终止状态的值为440;
2)跳过0,所有未就位的的数字到就位的曼哈顿距离的和。
(3)选择算子:一种改进的轮盘赌选择法
(4)交叉算子:两点交叉法,将两个染色体的某个区间交换
(5)编译算子:随机选择某连续几位,将各位取反
(6)精英遗传策略:直接将适应度高的个体遗传到下一代
(7)初代种群,通过随机产生
(8)所有的随机数都是通过rand()产生的,并设置srand((unsigned long)time(0))
(9)题目的输入、输出和poj1077 Eight的输入输出相同,同时输出最后迭代的代数,代数为最大遗传代数,表示没有在最大遗传代数中找到解
(10)变量参数的选择:
群体规模:PS = 150;
基因规模:GS = 140;
交叉概率:PC = 0.75;
交叉最大长度:CMaxLen = 20;
变异概率:PM = 0.09;
变异最大长度:MMaxLen = 10;
最大遗传代数:DEEP = 1000;
适应度最优值: SussessValue = 440;
精英遗传规模:DS = 10;
(11)遗传算法有很多需要优化的地方,我写的这个的效率不是很高,收敛的效果并不是很好,这里只是对遗传算法的思想进行了学习。
通过修改参数和选择不同的遗传算子,得到解的概率可以得到提高
遗传算法的八数码代码:
test的前几组可以再最大遗传代数内得到解的概率较大,后几组,在最大遗传代数内得到解的概率较小
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <cmath> #include <cstdlib> #include <fstream> #include <vector> #include <set> #include <algorithm> #include <time.h> #include <cstdlib> using namespace std; const double eps = 1e-10; /* 种群规模(P,population size):即种群中染色体个体的数目。 字串长度(l, string length) 交叉概率(pc, probability of performing crossover):控制着交叉算子的使用频率。交叉操作可以加快收敛, 使解达到最有希望的最优解区域,因此一般取较大的交叉概率,但交叉概率太高也可能导致过早收敛。 变异概率(pm, probability of mutation):控制着变异算子的使用频率,决定了遗传算法的局部搜索能力。 中止条件(termination criteria) */ const int PS = 150; const int GS = 140; const double PC = 0.75; const double PM = 0.09; const int DEEP = 1000; const double P_PS = 1.0 / PS; const int DS = 10; const int CMaxLen = 20; const int MMaxLen = 10; const int SucessValue = 440; bool findAns; int ansId, ansLen; struct node { int tab[3][3];///table int r, c;///0的位置 int h; }st, ed; int ed_map[10][2]; int dir_i[4] = {0, 0, 1, -1}; int dir_j[4] = {1, -1, 0, 0}; ///00 01 10 11 char print_op[4] = {'r', 'l', 'd', 'u'}; struct Chromsome{ string gene; double fitness; double p; bool operator<(const Chromsome &x) const { return fitness > x.fitness; } }; vector<Chromsome>population[2]; double psum[2]; int now, next; int nextn; inline int randomIdx(int x){ return rand()%x; } inline double random(){ return (double)rand()/RAND_MAX; } inline int check(int i, int j) { if (i > 2 || i < 0 || j > 2 || j < 0) return 0; return 1; } int get_h(node a) { int h = 0; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) if (a.tab[i][j] && a.tab[i][j] == ed.tab[i][j]) h += 100 - a.tab[i][j] * 10; return h; } int getFitnessValue(int id, node a) { int ret = 0; int nextr, nextc; string s = population[now][id].gene; for (int i = 0; i < GS; i += 2) { int x = (s[i] + s[i]) + s[i + 1]; nextr = a.r + dir_i[x]; nextc = a.c + dir_j[x]; if (check(nextr, nextc)) { swap(a.tab[nextr][nextc], a.tab[a.r][a.c]); swap(nextr, a.r); swap(nextc, a.c); ret = max(ret, get_h(a)); if (ret == SucessValue) { findAns = true; ansId = id; ansLen = i + 2; return ret; } } } return ret; } void calc() { psum[now] = 0; for (int i = 0; i < PS; i++) { population[now][i].fitness = getFitnessValue(i, st); if (findAns) return ; psum[now] += population[now][i].fitness; } sort(population[now].begin(), population[now].end()); for (int i = 0; i < PS; i++) population[now][i].p = (1.* population[now][i].fitness) / psum[now]; } void init() { srand((unsigned long)time(0)); // srand(1); population[0].clear(); population[1].clear(); now = 0; next = now ^ 1; for (int i = 0; i < PS; i++) { Chromsome ss; population[now].push_back(ss);/// population[next].push_back(ss); for (int j = 0; j < GS; j++) { population[now][i].gene += randomIdx(2); // cout << char('0' + population[now][i].gene[j]); // if ( (j + 1) % 2 == 0) cout << ' '; } // cout << endl; } } int Selection() { double nn = 0; double mm = random(); for (int i = 0; i < PS; i++) { nn += population[now][i].p; if (nn >= mm) { if (population[now][i].p < P_PS) population[now][i].p = 0; else population[now][i].p -= P_PS; return i; } } return PS - 1; } void crossover(int x, int y) { int low = randomIdx(GS); // int iup = randomIdx(GS); // if (ilow > iup) swap(ilow, iup); // cout << ilow << " Crossover " << iup << endl; // for (int i = ilow; i <= iup; i++) cout << char('0' + population[next][x].gene[i]) << ' '; // cout << endl; // for (int i = ilow; i <= iup; i++) cout << char('0' + population[next][y].gene[i]) << ' '; // cout << endl; int len = randomIdx(CMaxLen); len = min(len, GS - low - 1); for (int i = low; i <= low + len; i++) swap(population[next][x].gene[i], population[next][y].gene[i]); // for (int i = ilow; i <= iup; i++) cout << char('0' + population[next][x].gene[i]) << ' '; // cout << endl; // for (int i = ilow; i <= iup; i++) cout << char('0' + population[next][y].gene[i]) << ' '; // cout << endl; } void mutation(int x) { int i = randomIdx(GS); int low = x; int len = randomIdx(MMaxLen); len = min(len, GS - low - 1); // cout << x << " mutation " << char('0' + population[next][x].gene[i]) << ' '; for (int i = x; i < low + len; i++) population[next][x].gene[i] ^= 1; // cout << char('0' + population[next][x].gene[i]) << endl; } void out(string s, int len) { vector<int>ans; ans.clear(); int nextr, nextc; int r = st.r, c = st.c; for (int i = 0; i < len; i += 2) { int x = (s[i] + s[i]) + s[i + 1]; nextr = r + dir_i[x]; nextc = c + dir_j[x]; if (check(nextr, nextc)) { swap(nextr, r); swap(nextc, c); int vs = ans.size(); if (vs && ans[vs - 1] != x && ans[vs - 1] / 2 == x / 2) { ans.pop_back(); continue; } ans.push_back(x); } } for (int i = 0; i < ans.size(); i++) putchar(print_op[ans[i]]); puts(""); } void out_table(node a); void GA() { init(); int x, y; int dep = 0; findAns = 0, ansId = -1, ansLen = GS; // double pmm = 0.08; // double pcc = 0.8; while (dep != DEEP) { calc(); if (findAns) break; nextn = 0; for (int i = 0; i < DS; i++) population[next][nextn++] = population[now][i]; while (nextn < PS) { Chromsome tmp; int fla = 0; x = Selection(); y = Selection(); while(x == y) y = Selection(); population[next][nextn++] = population[now][x]; population[next][nextn++] = population[now][y]; double xx = random(); if (xx < PC) { crossover(nextn - 1, nextn - 2); } xx = random(); if (xx < PM) { for(int i = 0; i <= 2 * dep / 10; i++) mutation(nextn - 1); } xx = random(); if (xx < PM) { for(int i = 0; i <= 2 * dep / 10; i++) mutation(nextn - 2); } } // pcc -= 0.0005; // pmm += 0.0001; next = now; now ^= 1; dep++; } if (findAns) out(population[now][ansId].gene, ansLen); cout << "deep: " << dep << endl; } void solve() { if (get_h(st) == SucessValue) return ;///st == ed; else GA(); } void out_table(node a) { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) cout << a.tab[i][j] << ' '; cout <<endl; } cout <<endl; } void input(node &st) { char ch; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) { scanf(" %c", &ch); if (ch <= '9' && ch >= '0') st.tab[i][j] = ch - '0'; else { st.r = i; st.c = j; st.tab[i][j] = 0; } } } void In(char s[], node &st) { char ch; int next = 0; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) { while (s[next] == ' ') next++; ch = s[next++]; if (ch <= '9' && ch >= '0') st.tab[i][j] = ch - '0'; else { st.r = i; st.c = j; st.tab[i][j] = 0; } } } void pre() { for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) ed.tab[i][j] = (i * 3) + j + 1; ed.tab[2][2] = 0; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) if (ed.tab[i][j]) { ed_map[ed.tab[i][j]][0] = i; ed_map[ed.tab[i][j]][1] = j; } } int get_preval(node a) { int ret = 0; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) { if (!a.tab[i][j]) continue; int x = 0; for (int jj = j + 1; jj < 3; jj++) if (a.tab[i][jj] && a.tab[i][jj] < a.tab[i][j]) x++;/// for (int ii = i + 1; ii < 3; ii++) for (int jj = 0; jj < 3; jj++) if (a.tab[ii][jj] && a.tab[ii][jj] < a.tab[i][j]) x++; ret += x; } return ret % 2; } bool pre_solve() { return (get_preval(st) ^ get_preval(ed)); } int main() { pre(); char ss[12]; while (gets(ss)) { In(ss, st); // input(st); // input(ed); // out_table(st); // out_table(ed); if (pre_solve()) puts("unsolvable"); else solve(); } return 0; } /* x 2 3 1 4 6 7 5 8 1 2 3 x 4 6 7 5 8 1 2 3 4 5 x 7 8 6 1 2 3 4 x 8 7 6 5 x 4 3 2 1 8 7 6 5 2 3 4 1 5 x 7 6 8 1 8 4 3 0 5 7 2 6 7 1 2 8 0 3 6 4 5 4 1 5 8 0 2 7 6 3 4 1 0 3 8 6 7 2 5 4 6 5 7 3 1 0 8 2 7 1 2 6 0 8 4 5 3 7 1 5 6 0 2 3 4 8 3 5 1 8 7 2 4 6 0 6 7 2 5 0 4 8 1 3 */