遗传算法:N皇后
N皇后问题描述
N皇后问题是一个经典的问题,在一个N*N的棋盘上放置N个皇后,每行一个并使其不能互相攻击(同一行、同一列、同一斜线上的皇后都会自动攻击)。
遗传算法
遗传算法是局部束搜索的变形: 与自然选择过程相似,通过把两个父代结合产生后继(有性繁殖),而不是修改单一状态(无性繁殖)。
1、通过结合两个状态来产生后继状态
2、从k个随机产生的状态开始(种群)
3、状态表示成字符串(染色体)
4、评估函数(适应值函数)
5、通过选择(轮盘赌选择法)、交叉(杂交)和变异操作来产生新一代种群
6、半性繁殖的局部束搜索
7、适应值函数:相互攻击不到的皇后对数
轮盘赌选择法
轮盘赌选择法是依据个体的适应度值计算每个个体在子代中出现的概率,并按照此概率随机选择个体构成子代种群。轮盘赌选择策略的出发点是适应度值越好的个体被选择的概率越大。因此,在求解最大化问题的时候,我们可以直接采用适应度值来进行选择。但是在求解最小化问题的时候,我们必须首先将问题的适应度函数进行转换,以将问题转化为最大化问题。下面给出最大化问题求解中遗传算法轮盘赌选择策略的一般步骤:
(1) 将种群中个体的适应度值叠加,得到总适应度值==1 ,其中 为种群中个体个数。
(2) 每个个体的适应度值除以总适应度值得到个体被选择的概率
(3) 计算个体的累积概率以构造一个轮盘。
(4) 轮盘选择:产生一个[0,1]区间内的随机数,若该随机数小于或等于个体的累积概率且大于个体1的累积概率,选择个体进入子代种群。
重复步骤(4)次,得到的个体构成新一代种群。
图 3.4 染色体的轮盘赌式选择
伪代码:
//主循环
void Genetic::GeneticAlgorithm()
while m_NotSuccess为真
Select
Crossover
Mutate
End while
打印最优解
End
//计算一个state(棋盘)的适应值
//适应值采用“互相攻击皇后对的个数的倒数”,这比书上直接计算不互相攻击的皇后对数作为适应值的方法相比,更能拉开不同状态之间的差距。
//@para state:一个状态的引用
double Genetic::CalcuAdaptive(vector &state)
counter←0
For i: 0 to QueenNum-1 do
For i: 0 to QueenNum-1 do
If 对角线方向互相攻击,或者垂直方向互相攻击
counter++
End if
End for
End for
If counter等于0
m_NotSucess←false,程序的循环终止条件
m_BestOne←state,保存当前的状态
End if
Return 1.0/counter
End
//自然选择,大体思路是轮盘赌选择
void Genetic::Select()
创建一个新的空种群newPopulation
For i: 1 to populationSize-1 do
m_accumuAdaptive[i]←m_accumuAdaptive[i - 1] + m_adaptive[i]
End for
totalAdaptive←m_accumuAdaptive的最后一个元素
For i: 0 to populationSize-1 do
先把totalAdaptive(这是一个实数)放大成一个整数
产生一个随机数 ,对totalAdaptive求模,得到 ran
按相同比例缩小成一个实数
用二分查找的方法,在m_ accumuAdaptive内进行查找 ran,找出位置 j
把m_population的第 j 个状态push_back到newPopulation中
End for
m_population←newPopulation
End
杂交有多种思路:
- 选择两个state状态,随机产生一个杂交点,然后对这个杂交点的右(左)边的“基因”进行交换
- 选择两个state状态,随机产生一个杂交点,然后再对这个杂交点两边的“基因”都进行交换。
- 选择两个state状态,随机产生两个杂交点,然后再对这两个杂交点之间的“基因”进行交换。
变异:
通过伪随机数,使每一个基因有0.0几的概率进行突变。突变就是用伪随机数赋值。
代码实现
Genetic.h
#pragma once
#include <iostream>
#include <cstdlib>
#include <iomanip>
#include <algorithm>
#include <vector>
#include <ctime>
#include <cmath>
#include <fstream>
using namespace std; class Genetic {
private:
int m_Num; // 皇后数量
bool m_IsSuccess; // 是否成功找到最优解
vector<int>m_optimalSolution; // 最优解.
vector<vector<int> >m_population; // 种群
vector<double>m_adaptive; // 种群的适应值(1/冲突数)。
vector<double>m_AdaptValue; // 累积的适应值(定位哪一个被选中的) public:
Genetic(int numOfQueens, int initialGroupNum);
double CalcuAdaptive(vector<int> &state); // 计算适应值(互不相攻击的皇后对数)
void SetPopulation();
void Choose(); // 选择
void GeneticCrossover(); // 杂交
void GeneticMutate(); // 变异
void GeneticCalculation();
void Print(); // 打印最优解
};
Genetic.cpp
Genetic::Genetic(int numOfQueens, int initialGroupNum) {
m_adaptive.resize(initialGroupNum, );
m_AdaptValue.resize(initialGroupNum, );
m_Num = numOfQueens;
m_IsSuccess = true;
SetPopulation();
} void Genetic::SetPopulation() {
m_population.clear();
vector<int> tmpState(m_Num, );
for (int i = ; i < m_adaptive.size(); ++i) {
for (int j = ; j < m_Num; ++j) // 初始化
tmpState[j] = rand() % m_Num;
m_population.push_back(tmpState);
m_adaptive[i] = CalcuAdaptive(tmpState);
}
} double Genetic::CalcuAdaptive(vector<int> &state) {
int conflict = ;
for (int i = ; i < m_Num; ++i) {
for (int j = i + ; j < m_Num; ++j) {
//如果对角线方向互相攻击,或者垂直方向互相攻击
if (state[i] == state[j] || abs(state[i] - state[j]) == j - i)
conflict++;
}
}
if (conflict == ) { // 找到最优解
m_IsSuccess = false;
m_optimalSolution = state; //保存当前的状态
}
return 1.0 / conflict;
} //自然选择(大体思路是轮盘赌选择)
void Genetic::Choose() {
vector<vector<int>> NewPopulation; //创建一个新的空种群NewPopulation
m_AdaptValue[] = m_adaptive[];
for (int i = ; i < m_AdaptValue.size(); i++)
m_AdaptValue[i] = m_AdaptValue[i - ] + m_adaptive[i];
double totalAdaptive = m_AdaptValue[m_AdaptValue.size() - ];
//避免陷入局部最优解(不直接选择适应值最高的两个进行杂交)
for (int i = ; i < m_population.size(); i++) { //比例缩放的轮盘赌
int magnifyTotalAdaptive = totalAdaptive * ; //实数->整数(放大)
int random = (rand()*rand()) % magnifyTotalAdaptive;//转动轮盘
double select = (double)random / ; //按相同比例缩小 //int select = rand()*rand() % ((int)totalAdaptive);
vector<double>::iterator indexi;
//二分查找: 在m_AdaptValue中查找适应值与select最接近的个体的下标
indexi = lower_bound(m_AdaptValue.begin(), m_AdaptValue.end(), select);
int indexj = indexi - m_AdaptValue.begin();
//加入新的种群中
NewPopulation.push_back(m_population[indexj]);
}
// 更新种群
m_population.clear();
m_population = NewPopulation;
} void Genetic::GeneticCrossover() { //杂交==>交换基因片段(皇后位置进行交换)
int first = ;
int row1;
for (int i = ; i < m_population.size(); i++) {
if (rand() % ) {
++first;
if (first % == ) {
int crossPoint = rand() % (m_Num - );
for (int j = crossPoint; j < m_Num; j++)swap(m_population[row1][j], m_population[i][j]); //值交换
}
else row1 = i;
}
}
} void Genetic::Print() {
for (int i = ; i < m_optimalSolution.size(); ++i) {
for (int j = ; j < m_optimalSolution.size(); ++j) {
if (j == m_optimalSolution[i]) cout << "Q ";
else cout << ". ";
}cout << '\n';
}cout << '\n';
} //随机突变=> 随机改变某个个体的某个基因(随机改变某个地图中的某个皇后的位置)
void Genetic::GeneticMutate() {
int mutateSpot = ;
for (int i = ; i < m_population.size(); ++i) {
if (rand() % == ) {
mutateSpot = rand() % m_Num;
m_population[i][mutateSpot] = rand() % m_Num;
}
m_adaptive[i] = CalcuAdaptive(m_population[i]); // 更新适应值
}
} void Genetic::GeneticCalculation() {
clock_t start, finish;
start = clock(); while (m_IsSuccess) {
// 自然选择
Choose();
// 杂交
GeneticCrossover();
// 变异
GeneticMutate();
}
//打印最优解
//if (m_Num<25) Print();
Print();
finish = clock();
cout << "遗传算法求解时间: " << finish - start << "ms" << endl;
}
main.cpp
#include"Genetic.h"
int main() {
int numOfQueen;
cout << "请输入皇后数目: ";
cin >> numOfQueen;
cout << "【遗传算法求解】" << endl;
Genetic Q(numOfQueen, );
Q.GeneticCalculation();
system("pause");
}
运行结果:
遗传算法:N皇后的更多相关文章
- 简单遗传算法求解n皇后问题
版权声明:本文为博主原创文章,转载请注明出处. 先解释下什么是8皇后问题:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法.在不 ...
- 【译】N 皇后问题 – 构造法原理与证明 时间复杂度O(1)
[原] E.J.Hoffman; J.C.Loessi; R.C.Moore The Johns Hopkins University Applied Physics Laboratory *[译]* ...
- 递归实现n(经典的8皇后问题)皇后的问题
问题描述:八皇后问题是一个以国际象棋为背景的问题:如何能够在8×8的国际象棋棋盘上放置八个皇后, 使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行.纵行或斜线上 ...
- 八皇后算法的另一种实现(c#版本)
八皇后: 八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例.该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于 ...
- [LeetCode] N-Queens II N皇后问题之二
Follow up for N-Queens problem. Now, instead outputting board configurations, return the total numbe ...
- [LeetCode] N-Queens N皇后问题
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens ...
- N皇后问题—初级回溯
N皇后问题,最基础的回溯问题之一,题意简单N*N的正方形格子上放置N个皇后,任意两个皇后不能出现在同一条直线或者斜线上,求不同N对应的解. 提要:N>13时,数量庞大,初级回溯只能保证在N< ...
- 用遗传算法GA改进CloudSim自带的资源调度策略(2)
遗传算法GA的核心代码实现: 最核心: private static ArrayList<int[]> GA(ArrayList<int[]> pop,int gmax,dou ...
- 用遗传算法GA改进CloudSim自带的资源调度策略
首先理解云计算里,资源调度的含义: 看了很多云计算资源调度和任务调度方面的论文,发现很多情况下这两者的意义是相同的,不知道这两者是同一件事的不同表述还是我没分清吧,任务调度或者资源调度大概就是讲这样一 ...
随机推荐
- 02-移动端开发教程-CSS3新特性(中)
1. 新的背景 背景在CSS3中也得到很大程度的增强,比如背景图片尺寸.背景裁切区域.背景定位参照点.多重背景等. 1.1 background-size设置背景图片的尺寸 cover会自动调整缩放比 ...
- Spring Boot 配置文件详解
Spring Boot配置文件详解 Spring Boot提供了两种常用的配置文件,分别是properties文件和yml文件.他们的作用都是修改Spring Boot自动配置的默认值.相对于prop ...
- 增加Linux虚拟机的硬盘空间
原配置为40G,现需要增加到60G,操作方法如下: 一.虚拟机关机,在编辑设置里调整硬盘空间到60G 二.虚拟机开机,扩展硬盘空间 1.安装gparted,命令如下 sudo apt-get inst ...
- MHA 安装与简单使用
MHA 在过去几年一直用的比较火,特别是在在传统复制的那个年代.至从有了GTID好像我们也可以把MHA给忘记了,但是很多企业现在还是在用的比较多.每个公司的MHA玩法也不太一样,但是本质都是差不多了. ...
- python之celery的使用(一)
前段时间需要使用rabbitmq做写缓存,一直使用pika+rabbitmq的组合,pika这个模块虽然可以很直观地操作rabbitmq,但是官方给的例子太简单,对其底层原理了解又不是很深,遇到很多坑 ...
- VMware虚拟机安装
学习Linux系统最好的方式就是在自己的虚拟机上安装Linux:接下来就给大家简单介绍一下VMware虚拟机的安装以及Linux的安装:VMware虚拟机只是为了更好的学习Linux: ...
- Extensions in UWP Community Toolkit - ViewExtensions
概述 UWP Community Toolkit Extensions 中有一个为 View 提供的扩展 - View Extensions,本篇我们结合代码详细讲解 View Extensions ...
- PHP 抓取网页内容的几个函数
<?php //获取所有内容url保存到文件 function get_index($save_file, $prefix="index_"){ $count = 68; $ ...
- 用委托(Delegate)的BeginInvoke和EndInvoke方法操作线程
让我们首先了解下什么时候用到C#异步调用: .NET Framework 允许您C#异步调用任何方法.定义与您需要调用的方法具有相同签名的委托:公共语言运行库将自动为该委托定义具有适当签名的Begin ...
- iOS之AFSecurityPolicy
AFSecurityPolicy是AFNetworking中负责对https请求进行证书验证的模块,本文主要是要搞清楚它是如何工作的. 在介绍AFSecurityPolicy之前,我们先来了解一下ht ...