C语言局部搜索算法(爬山法,模拟退火法,遗传算法)求解八皇后问题
C語言局部算法求解八皇后問題
- 寫在前面
- 八皇后問題及局部搜索算法
- 爬山法(hill-climbing searching)
- 算法介紹
- 代碼實現(xiàn)
- 退火法(simulated annealing)
- 算法介紹
- 代碼實現(xiàn)
- 遺傳算法
- 算法介紹
- 代碼實現(xiàn)
寫在前面
該篇博客改自https://blog.csdn.net/chenxz_/article/details/83014641,習(xí)慣用python可以去參考他的代碼。
八皇后問題及局部搜索算法
八皇后問題(英文:Eight queens),是由國際西洋棋棋手馬克斯·貝瑟爾于1848年提出的問題,是回溯算法的典型案例。
問題表述為:在8×8格的國際象棋上擺放8個皇后,使其不能互相攻擊,即任意兩個皇后都不能處于同一行、同一列或同一斜線上,問有多少種擺法。高斯認為有76種方案。1854年在柏林的象棋雜志上不同的作者發(fā)表了40種不同的解,后來有人用圖論的方法解出92種結(jié)果。如果經(jīng)過±90度、±180度旋轉(zhuǎn),和對角線對稱變換的擺法看成一類,共有42類。計算機發(fā)明后,有多種計算機語言可以編程解決此問題。
局部搜索算法是一種簡單的貪心搜索算法,是解決最優(yōu)化問題的一種啟發(fā)式算法,該算法每次從當(dāng)前解的臨近解空間中根據(jù)啟發(fā)函數(shù)選擇一個最優(yōu)解(也不一定是最優(yōu)解)作為當(dāng)前解,直到達到一個局部最優(yōu)解。本文以求解八皇后問題來描述爬山法,模擬退火法以及遺傳算法。
爬山法(hill-climbing searching)
算法介紹
爬山法是指經(jīng)過評價當(dāng)前的問題狀態(tài)后,限于條件去增加這一狀態(tài)與目標(biāo)狀態(tài)的差異,經(jīng)過迂回前進,最終達到解決問題的總目標(biāo)。就如同爬山一樣,為了到達山頂,有時不得不先上矮山頂,然后再下來,這樣翻越一個個的小山頭,直到最終達到山頂。可以說,爬山法是一種"以退為進"的方法,往往具有"退一步進兩步"的作用,后退乃是為了更有效地前進。爬山法也叫逐個修改法、瞎子摸象法。
代碼實現(xiàn)
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<math.h>
#include<string.h>//初始化八皇后的布局
int inital(int *status)
{int i,row;for(i=0;i<8;i++){row=rand()%8;status[i]=row;}return 0;
}//定義了兩種方式展示八皇后布局
void display(int *status)
{int i;for(i=0;i<8;i++)printf("%d ",status[i]);printf("\n");/*for(i=0;i<8;i++){for(j=0;j<8;j++){if(status[i]!=j)printf("%d ",0);elseprintf("%d ",1);}printf("\n");}*/
}//得到八皇后有沖突的數(shù)目
int num_of_conflict(int *status)
{int num_of_conflict=0;int i,j;for(i=0;i<7;i++){for(j=i+1;j<8;j++){if((status[i]==status[j])||((j-i)==abs(status[i]-status[j])))num_of_conflict++;}}return num_of_conflict;
}//將一個八皇后布局拷貝給另一個
void copy(int *in, int *out)
{int i;for(i=0;i<8;i++)out[i]=in[i];
}//比較兩個八皇后布局是否相同
int compare(int *status1,int *status2)
{int i;for(i=0;i<8;i++){if(status1[i]!=status2[i])return 0;}return 1;
}//改變布局,得到最小沖突的分布,即爬山的應(yīng)用
int *get_min_conflict(int *status)
{int i,j,choose;int *min_status=(int*)malloc(sizeof(int)*9);memset(min_status,0,sizeof(int)*9);int new_status[8]={0};copy(status,min_status);for(i=0;i<8;i++){for(j=0;j<8;j++){copy(status,new_status);if(status[i]!=j){new_status[i]=j;if(num_of_conflict(new_status)<num_of_conflict(min_status))copy(new_status,min_status);else if(num_of_conflict(new_status)==num_of_conflict(min_status)&&num_of_conflict(new_status)!=num_of_conflict(status)){choose=rand()%2;if(choose==1)copy(new_status,min_status);}}}}return min_status;
}int main()
{int status[8]={0};int step=0,max_step=8;int *new_status=(int*)malloc(8*sizeof(int));memset(new_status,0,sizeof(int)*8);srand((unsigned)time(NULL));inital(status);printf("initual status:\n");display(status);printf("the num of conflict: %d\n\n",num_of_conflict(status));while(num_of_conflict(status)&&step<max_step){step++;new_status=get_min_conflict(status);;if(compare(new_status,status)){printf("the best answer:\n");display(status);printf("the num of conflict: %d\ncan not find an answer!\n",num_of_conflict(status));break;}copy(new_status,status);printf("the new status:\n");display(status);printf("the num of conflict: %d\n\n",num_of_conflict(status));if(num_of_conflict(status)==0)printf("find answer!\n");}getchar();return 0;
}
效果展示:
失敗案例:
成功案例:
退火法(simulated annealing)
算法介紹
模擬退火算法(Simulated Annealing,SA)最早的思想是由N. Metropolis [1] 等人于1953年提出。1983 年,S. Kirkpatrick 等成功地將退火思想引入到組合優(yōu)化領(lǐng)域。它是基于Monte-Carlo迭代求解策略的一種隨機尋優(yōu)算法,其出發(fā)點是基于物理中固體物質(zhì)的退火過程與一般組合優(yōu)化問題之間的相似性。模擬退火算法從某一較高初溫出發(fā),伴隨溫度參數(shù)的不斷下降,結(jié)合概率突跳特性在解空間中隨機尋找目標(biāo)函數(shù)的全局最優(yōu)解,即在局部最優(yōu)解能概率性地跳出并最終趨于全局最優(yōu)。模擬退火算法是一種通用的優(yōu)化算法,理論上算法具有概率的全局優(yōu)化性能,目前已在工程中得到了廣泛應(yīng)用,諸如VLSI、生產(chǎn)調(diào)度、控制工程、機器學(xué)習(xí)、神經(jīng)網(wǎng)絡(luò)、信號處理等領(lǐng)域。
代碼實現(xiàn)
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<math.h>
#include<string.h>int inital(int *status)
{int i,j;for(i=0;i<8;i++){j=rand()%8;status[i]=j;}return 0;
}void display(int *status)
{int i;for(i=0;i<8;i++)printf("%d ",status[i]);printf("\n");/*for(i=0;i<8;i++){for(j=0;j<8;j++){if(status[i]!=j)printf("%d ",0);elseprintf("%d ",1);}printf("\n");}*/
}int num_of_conflict(int *status)
{int num_of_conflict=0;int i,j;for(i=0;i<7;i++){for(j=i+1;j<8;j++){if((status[i]==status[j])||((j-i)==abs(status[i]-status[j])))num_of_conflict++;}}return num_of_conflict;
}void copy(int *in, int *out)
{int i;for(i=0;i<8;i++)out[i]=in[i];
}int compare(int *status1,int *status2)
{int i;for(i=0;i<8;i++){if(status1[i]!=status2[i])return 0;}return 1;
}//獲取下一個布局,隨機的
int *get_next_status(int *status, double T)
{ int i,j;int flag=0;int choice;int new_status[8]={0};int **next_status=(int**)malloc(sizeof(int*)*56);for(i=0;i<56;i++){next_status[i]=(int*)malloc(sizeof(int)*9);memset(next_status[i],0,sizeof(int)*9);}for(i=0;i<8;i++){for(j=0;j<8;j++){copy(status,new_status);if(status[i]!=j){new_status[i]=j;copy(new_status,next_status[flag++]);} }}choice=rand()%56;if(num_of_conflict(next_status[choice])<=num_of_conflict(status)){return next_status[choice];}else{double E=num_of_conflict(status)-num_of_conflict(next_status[choice]);double probability=exp(E/T);double choose=(double)(rand()%999)/1000.0;if(choose<=probability){return next_status[choice];}}return status;
}int main()
{double T=5.0;int*status=(int*)malloc(sizeof(int)*9);memset(status,0,sizeof(int)*9);int*new_status=(int*)malloc(sizeof(int)*9);memset(new_status,0,sizeof(int)*9);srand((unsigned)time(NULL));inital(status);printf("the initial status:\n");display(status);printf("the num of conflict: %d\n\n",num_of_conflict(status));while(num_of_conflict(status)){new_status=get_next_status(status,T);if(compare(new_status,status)) printf("it does not move\n");else{copy(new_status,status);printf("the new status:\n");display(status);printf("the num of conflict: %d\n\n",num_of_conflict(status));if(num_of_conflict(status)==0)printf("find answer!\n");}T=T*0.99;if(T<0.0001){printf("max try, can not find an answer\n");break;}}getchar();return 0;
}
一般來說爬山法都能得到成功布局
代碼效果:
遺傳算法
算法介紹
遺傳算法(Genetic Algorithm,GA)最早是由美國的 John holland于20世紀(jì)70年代提出,該算法是根據(jù)大自然中生物體進化規(guī)律而設(shè)計提出的。是模擬達爾文生物進化論的自然選擇和遺傳學(xué)機理的生物進化過程的計算模型,是一種通過模擬自然進化過程搜索最優(yōu)解的方法。該算法通過數(shù)學(xué)的方式,利用計算機仿真運算,將問題的求解過程轉(zhuǎn)換成類似生物進化中的染色體基因的交叉、變異等過程。在求解較為復(fù)雜的組合優(yōu)化問題時,相對一些常規(guī)的優(yōu)化算法,通常能夠較快地獲得較好的優(yōu)化結(jié)果。遺傳算法已被人們廣泛地應(yīng)用于組合優(yōu)化、機器學(xué)習(xí)、信號處理、自適應(yīng)控制和人工生命等領(lǐng)域。
代碼實現(xiàn)
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<math.h>
#include<string.h>int inital(int *status)
{int i,j;for(i=0;i<8;i++){j=rand()%8;status[i]=j;}return 0;
}void display(int *status)
{int i;for(i=0;i<8;i++)printf("%d ",status[i]);printf("\n");/*for(i=0;i<8;i++){for(j=0;j<8;j++){if(status[i]!=j)printf("%d ",0);elseprintf("%d ",1);}printf("\n");}*/
}//展示種群所有布局
void display_all(int**all_status)
{int i;for(i=0;i<4;i++){display(all_status[i]);}
}//展示種群中各布局的沖突,調(diào)試的時候用的,代碼運行不需要
void display_noConflict(int *noConflict)
{int i;for(i=0;i<4;i++){printf("%d ",noConflict[i]);}printf("\n");
}int num_of_conflict(int *status)
{int num_of_conflict=0;int i,j;for(i=0;i<7;i++){for(j=i+1;j<8;j++){if((status[i]==status[j])||((j-i)==abs(status[i]-status[j])))num_of_conflict++;}}return num_of_conflict;
}//得到種群中最小的沖突數(shù)
int get_minConflict(int **all_status)
{int min=999;int i;for(i=0;i<4;i++){if(num_of_conflict(all_status[i])<min)min=num_of_conflict(all_status[i]);}return min;
}int num_of_noConflict(int*status)//我也不知道為什么要定義這個函數(shù),有num_of_conlict好像就夠了
{return 28-num_of_conflict(status);
}void copy(int *in, int *out)
{int i;for(i=0;i<8;i++)out[i]=in[i];
}int compare(int *status1,int *status2)
{int i;for(i=0;i<8;i++){if(status1[i]!=status2[i])return 0;}return 1;
}//獲得種群中所有個體沖突數(shù)集合
int get_sum(int*num)
{int i;int sum=0;for(i=0;i<4;i++)sum+=num[i];return sum;
}//隨機返回種群中四個個體中的一個
int *get_parent(int**all_status,int*noConflict) //我也不知道為什么要這么寫,完全可以直接調(diào)用rand()返回0到3的隨機數(shù),可能是為了看起來更像遺傳吧
{int choice=rand()%get_sum(noConflict);if(choice<noConflict[0])return all_status[0];else if(choice>=noConflict[0]&&choice<(noConflict[0]+noConflict[1]))return all_status[1];else if(choice>=(noConflict[0]+noConflict[1])&&choice<(noConflict[0]+noConflict[1]+noConflict[2]))return all_status[2];return all_status[3];
}//種群中個體隨機變異
int **variation(int **all_status)
{int i,col,row;for(i=0;i<4;i++){row=rand()%8;col=rand()%8;all_status[i][row]=col;}return all_status;
}//種群中個體遺傳
int **inheritance(int **all_status)
{int flag=0;int i,j,num;int *father,*mother;int child1[8]={0};int child2[8]={0};int *noConflict=(int*)malloc(sizeof(int)*5);memset(noConflict,0,sizeof(int)*5);int **new_all_status=(int**)malloc(sizeof(int*)*4);for(i=0;i<4;i++){new_all_status[i]=(int*)malloc(sizeof(int)*9);memset(new_all_status[i],0,sizeof(int)*5);noConflict[i]=num_of_noConflict(all_status[i]);}for(i=0;i<2;i++){father=get_parent(all_status,noConflict);mother=get_parent(all_status,noConflict);while(compare(father,mother))mother=get_parent(all_status,noConflict);copy(father,child1);copy(mother,child2);num=rand()%7;for(j=0;j<num+1;j++){child1[j]=child2[j];child2[j]=father[j];}copy(child1,new_all_status[flag++]);copy(child2,new_all_status[flag++]);}return new_all_status;
}//種群中是否有成功布局
int find_answer(int**all_status)
{int i;for(i=0;i<4;i++){if(num_of_noConflict(all_status[i])==28){printf("find answer!\n");display(all_status[i]);return 1;}}return 0;
}int main()
{int i;srand((unsigned)time(NULL));int **all_status=(int**)malloc(sizeof(int*)*4);for(i=0;i<4;i++){all_status[i]=(int*)malloc(sizeof(int)*9);memset(all_status[i],0,9);inital(all_status[i]);}printf("the inital all_status:\n");display_all(all_status);printf("the min all_status: %d\n\n",get_minConflict(all_status));all_status=inheritance(all_status);int vari_prob;while(!find_answer(all_status)){vari_prob=rand()%11;if(vari_prob==1){all_status=variation(all_status);printf("have a variation, and the all_status:\n");display_all(all_status);printf("the min conflict: %d\n\n",get_minConflict(all_status));}else{all_status=inheritance(all_status);printf("the next all_status:\n");display_all(all_status);printf("the min conflict: %d\n\n",get_minConflict(all_status));}}
}
代碼效果展示(目前還在演化):
總結(jié)
以上是生活随笔為你收集整理的C语言局部搜索算法(爬山法,模拟退火法,遗传算法)求解八皇后问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言中连续调用rand函数,返回值不变
- 下一篇: Matlab实现连通域标记算法求图像连通