Something about Eight Queens Problem

八皇后问题 Eight Queens Problem

1. 规则介绍 Rule Introduction

在国际象棋中,皇后(Queen)可以横行竖走,还可以进行对角线方向的行动,可以说是国际象棋中能力最强的棋子。
现在,在一个8x8的棋盘上,我们希望放入8个皇后棋子,使得每个皇后都不会互相攻击到。用算法的话,可以怎么解决这个问题?

2. 解决方法 Implemented with Java

2.1 爬山算法—Hill-climbing

————————————————————————————————————————————

2.1.1 算法介绍 Introduction
爬山算法是希望每次走下一步,都往使得当前状态得到改善的方向前进,也就是说,当我们从起始状态出发时,我们总是希望往山上走,而不是山下。对于八皇后问题而言,这种改善体现在总体冲突的减少。因此,我们在爬山算法中,找出冲突最小的皇后的位置进行loose操作。
而对于八皇后问题,我们在初始化的时候,一般都是选择按照每列或者是每行一个皇后的方式来初始化。所以在选择下一步的时候,我们通常只需要在当前该列或者该行中选出冲突最小的位置,作为下一步的选择。
然而爬山算法并不是完备(Complete)的,显然如果两个方向都可以使得我们当前的状态得到改善,那我们必然会优先选择使得这种改善达到最好的方向前进,然而这并不代表我们最终会走到最优的结果。
所以,当我们发现我们已经无法找到一个前进方向得到一个比当前更优的状态时,这并不代表我们已经解决了问题,此时我们还需要判断,是否每个皇后的位置还有冲突存在。如果是这样,那么这是一次失败的演算,我们可以考虑
2.1.2 数据结构 Data Structure

由于维护两个二维数组的成本较大,所以我选择构造了Queen这个类,如图是这个类的三个主要成员变量,x,y分别代表queen在棋盘上的横纵坐标,而degree则代表queen在(x, y)这个位置与她产生冲突的其他棋子个数
在这里插入图片描述
然后,选择用一个优先队列来存储这八个queen,重写比较器,使得这个优先队列的对头将会是冲突最多的queen
在这里插入图片描述

2.1.3 代码关键函数实现 Implementation
solution函数

solution函数是对Solution接口的solution函数的具体实现,是算法的主体,主要分为几步:

  • 找出冲突最大的棋子(也就是队列首的queen)
  • 移动棋子到同一行冲突最小的那个位置(在计算空位的冲突度数的时候,应该把同一行,也就是我们将要去移动的目标棋子去掉,也就是得到的度数应该要减1)
  • 如果队列最前的棋子的冲突度数为0,说明当前状态已经没有冲突,解决问题
  • 如果冲突度数不为0,并且找不到冲突更小的位置进行移动,则演算失败
    在这里插入图片描述
getConflictDegree函数

计算所给位置的冲突度数(最大为8,因为这里并没有忽略同一行已经存在的棋子)
在这里插入图片描述

move函数

在进行移动之前,确认冲突情况,如果和一个棋子冲突情况前后不变,则该棋子不需要修改冲突度,如果冲突情况改变,变为冲突,则该棋子的冲突度加1,如果变为不冲突,则棋子的冲突度减1。而移动的棋子的冲突度则只需要遍历比较,如果冲突则度数加1。由于队列的第一个棋子是我们所要比较的所以跳过。
在这里插入图片描述

2.1.4 输出结果 Output

在这里插入图片描述

2.2 模拟退火算法—Simulated Annealing

————————————————————————————————————————————

2.2.1 算法介绍 Introduction
退火算法相比较于爬山算法,就是在算法主体中,在爬山算法的下一步选择的方式上作出了改变。随机选择一个位置,同样,我们依然是希望往状态得到改善的方向去移动,但是,我们已经知道,这同时也是爬山算法的局限性,因为这样的移动方式并不能保证算法的完备性。也就是说我们并不能忽略往山下走的路。这就是退火算法的思想。当下一个状态是更优的状态是,同样,我们依然会选择往那个方向移动;然而如果下一个状态是一个更坏的选择的话,我们也不会彻底拒绝他,这时我们依据一个概率模型,根据当前的一个参数T得到一个概率,如果比这个概率小,那么我们将进行冒险的行为,也就是接受这个更坏的选择。而随着算法推进,T的值将会变小。
2.2.2 数据结构 Data Structure

采用与爬山算法一样的数据结构,但是这里需要额外保存整个棋盘的信息,因为位置是随机选择的,只需要在移动的时候更新棋盘信息,也就是把将要移动的位置设置为1即可,可以接受

2.2.3 代码关键函数实现 Implementation
solution函数
  • 同样,找到冲突度最大的棋子
  • 然后通过一个随机数,随机找一个位置(如果那里有空位的话)
  • 如果冲突减少,前往该位置
  • 否则,根据概率,和产生的随机概率做比较,选择是否接受这个更坏的决定
  • 完备则结束,成功
  • 否则,当参数T比0.00001小,退出,演算失败
    在这里插入图片描述
2.2.4 输出结果 Output

在这里插入图片描述

2.3 基因算法 Genetic

————————————————————————————————————————————

2.3.1 算法介绍 Introduction
遗传算法的主要思想是通过增加变化,来增加可能性。它有一个重要的思想,就是对当前状态用编码的方式来进行描述。这种方式使得在某些情况下我们可以跳出当前我们所无法预知的坏的状态。
我们通过计算每个个体中,互相不冲突的基因对数——也就是对于我们所要解决的问题中,互相 不冲突的皇后的对数(组合,而不是排列),而根据每个个体的互补冲突的对数,计算其对数占总对数的概率,概率越大,越容易被选中。我们可以通过一个随机数,通过这个随机数所落在的区间,判断哪个个体被挑选到。显然,这个时候随机数产生的范围应该是0到所有个体的适应值到和。而在挑选第二个个体的时候,这时候的上界自然就是不含被挑选出来的第一个个体的所有个体的适应值的和了
被优先选中的两个个体,就会进入下一个步骤:交叉(Cross Over)。这个步骤,同样是依靠随机数,产生一个交叉点,如下图为例,亲代将以交叉点为界,分为两段,交换两者相互相对应的一段,这个时候,就从两个亲代得到了两个子代
最后,进行变异(Mutation)。随机选择一个个体的一个基因随机改变。这个操作可以很好的避免我们的算法逐渐在我们毫不知觉的情况下走向死路。但同时,也很有可能南辕北辙,绕了更远的路。

2.3.2 数据结构 Data Structure

分别用两个数组(这里我用了比较不符合java风格的方法,因为懒得再写一个类了。。。)
private String[] chroms = new String[k];一个记录每个个体编码的字符串数组
private int[] values = new int[k];还有一个记录对应于上面的数组的字符串所对应的适应值

2.3.3 代码关键函数实现 Implementation
solution函数在这里插入图片描述
crossOver函数

使用java的String类的substring函数,交换子串拼接
在这里插入图片描述

variation函数

同样是使用String类的substring函数,交换子串拼接,中间插入更改的基因的值
在这里插入图片描述

2.3.4 输出结果 Output

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值