C语言局部算法求解八皇后问题

  • 写在前面
  • 八皇后问题及局部搜索算法
  • 爬山法(hill-climbing searching)
    • 算法介绍
    • 代码实现
  • 退火法(simulated annealing)
    • 算法介绍
    • 代码实现
  • 遗传算法
    • 算法介绍
    • 代码实现

写在前面

该篇博客改自https://blog.csdn.net/chenxz_/article/details/83014641,习惯用python可以去参考他的代码。

八皇后问题及局部搜索算法

八皇后问题(英文:Eight queens),是由国际西洋棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例。
问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。如果经过±90度、±180度旋转,和对角线对称变换的摆法看成一类,共有42类。计算机发明后,有多种计算机语言可以编程解决此问题。

局部搜索算法是一种简单的贪心搜索算法,是解决最优化问题的一种启发式算法,该算法每次从当前解的临近解空间中根据启发函数选择一个最优解(也不一定是最优解)作为当前解,直到达到一个局部最优解。本文以求解八皇后问题来描述爬山法,模拟退火法以及遗传算法。

爬山法(hill-climbing searching)

算法介绍

爬山法是指经过评价当前的问题状态后,限于条件去增加这一状态与目标状态的差异,经过迂回前进,最终达到解决问题的总目标。就如同爬山一样,为了到达山顶,有时不得不先上矮山顶,然后再下来,这样翻越一个个的小山头,直到最终达到山顶。可以说,爬山法是一种"以退为进"的方法,往往具有"退一步进两步"的作用,后退乃是为了更有效地前进。爬山法也叫逐个修改法、瞎子摸象法。

代码实现

#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");}*/
}//得到八皇后有冲突的数目
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_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 等成功地将退火思想引入到组合优化领域。它是基于Monte-Carlo迭代求解策略的一种随机寻优算法,其出发点是基于物理中固体物质的退火过程与一般组合优化问题之间的相似性。模拟退火算法从某一较高初温出发,伴随温度参数的不断下降,结合概率突跳特性在解空间中随机寻找目标函数的全局最优解,即在局部最优解能概率性地跳出并最终趋于全局最优。模拟退火算法是一种通用的优化算法,理论上算法具有概率的全局优化性能,目前已在工程中得到了广泛应用,诸如VLSI、生产调度、控制工程、机器学习、神经网络、信号处理等领域。

代码实现

#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世纪70年代提出,该算法是根据大自然中生物体进化规律而设计提出的。是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。该算法通过数学的方式,利用计算机仿真运算,将问题的求解过程转换成类似生物进化中的染色体基因的交叉、变异等过程。在求解较为复杂的组合优化问题时,相对一些常规的优化算法,通常能够较快地获得较好的优化结果。遗传算法已被人们广泛地应用于组合优化、机器学习、信号处理、自适应控制和人工生命等领域。

代码实现

#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]);}
}//展示种群中各布局的冲突,调试的时候用的,代码运行不需要
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;
}//得到种群中最小的冲突数
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)//我也不知道为什么要定义这个函数,有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;
}//获得种群中所有个体冲突数集合
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) //我也不知道为什么要这么写,完全可以直接调用rand()返回0到3的随机数,可能是为了看起来更像遗传吧
{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));}}
}

代码效果展示(目前还在演化):

C语言局部搜索算法(爬山法,模拟退火法,遗传算法)求解八皇后问题相关推荐

  1. 八皇后问题遗传算法c语言,遗传算法解决八皇后问题

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? 八皇后问题描述 19 世纪著名的数学家 Gauss 在 1850 年提出八皇后问题后, 该问题成为各类语言程序设计的经典 ...

  2. 随机优化算法---爬山法VS模拟退火法

    随机优化算法–爬山法VS模拟退火算法 随机优化算法,由于开始和过程都是随机的数值,所以每次产生的结果都不一样.但大致收敛方向是一致的. 爬山法是一种局部最优的算法(本质上属于贪心法),也属于启发式的方 ...

  3. poj 1379 模拟退火法

    /* 模拟退火法:找到一些随机点,从这些点出发,随机的方向坐标向外搜索:最后找到这些随机点的最大值:坑://if(xx>-eps&&xx<x+eps&&yy ...

  4. 爬山法求解八皇后问题的全部解法

    爬山法求解八皇后问题的全部解法 程序的概要设计思想 初始状态 冲突函数 寻找邻居状态 寻找全部解集 程序主要函数的作用 运行结果截图 Python源代码 程序的概要设计思想 爬山算法是一种局部贪婪算法 ...

  5. 【学习笔记】我命由天不由我之随机化庇佑 —— 爬山法 和 模拟退火法

    以下均假设最优解是在最低点. 爬山法 爬山算法是一种局部择优的方法,采用启发式方法,是对深度优先搜索的一种改进,它利用反馈信息帮助生成解的决策. 直白地讲,就是当目前无法直接到达最优解,但是可以判断两 ...

  6. 爬山搜索法c语言代码,搜索算法--爬山法 (代码示例)

    以航班为例,从出发城市到目标城市区域内各段最远的航班 //1.程序入口 static void Main(string[] args) { Test.SampleOne sample = new Te ...

  7. 爬山法实现 八皇后问题 (Python 实现)

    本文主要简单阐述爬山法的基本算法思想,并给出用此算法实现八皇后问题详细过程 最基本的爬上搜索算法表示:(节选自<人工智能>第二版): function HILL-CLIMBING(prob ...

  8. JAVA用爬山法解决八皇后问题_局部搜索算法.ppt

    局部搜索算法 人工智能原理第2章 搜索技术(下) 本章内容2.1 搜索与问题求解2.2 无信息搜索策略2.3 启发式搜索策略2.4 局部搜索算法2.5 约束满足问题2.6 博弈搜索参考书目附录 A*算 ...

  9. 【人工智能】—局部搜索算法、爬山法、模拟退火、局部剪枝、遗传算法

    Local search algorithms (局部搜索算法) 局部搜索算法 内存限制 局部搜索算法 示例:n-皇后 爬山算法 随机重启爬山 模拟退火算法 局部剪枝搜索 遗传算法 小结 局部搜索算法 ...

最新文章

  1. Python设计模式-解释器模式
  2. gdb php-fpm,使用 gdb 调试 php-fpm 异常错误
  3. Docker compose多容器管理
  4. SQL Plan Management介绍
  5. 我也学习JAVA多线程-join
  6. DM8168学习--内存烧写位置
  7. android标题返回,【Android开发】自定义控件——带返回键标题栏
  8. NET问答: 枚举 和 常量 在使用时该怎么抉择?
  9. libcudart.so.8.0 cannot open shared object file: No such file or directory
  10. 设计干货栅格系统素材 | UI设计师应用好帮手
  11. my android机器人作文,机器人作文400字
  12. 离线缓存与客户端存储总结
  13. 鸿蒙方舟UI开发框架-eTS状态管理
  14. 充电宝买哪种比较好?评价最好的充电宝推荐
  15. 编写选择结构程序,输入个人月收入总额,计算出他本月应缴税款和税后收入
  16. UGC、PGC、OGC
  17. Java 面试 :乐观锁 悲观锁
  18. 如何将您的智能手机用作Amazon Fire TV遥控器
  19. html 如何去掉超链接下的下划线
  20. Linux中分卷压缩和合并解压

热门文章

  1. 安卓串口中InputStream数据接收不完整
  2. day07-字符编码、文件操作
  3. Word文档以两列的格式打开,类似于书本那样
  4. 简介SharePoint 2010 14 Hive文件夹
  5. 生产环境项目问题记录系列(一):一次循环数据库拖垮服务器问题
  6. unity开发小贴士之八 Audio使用心得
  7. Chapter 4 Invitations——4
  8. Ubuntu 中的 dpkg 安装deb、删除deb、显示已安装包列表、解压deb文件、显示deb包内文件列表、配置deb软件包
  9. 判断一个字符串中出现次数最多的字符
  10. Android 5.0新特性之沉浸式状态栏