一、实验目的
1. 熟悉随机化算法的基本思想;
2. 使用随机化算法分析、解决问题;
3. 实现算法。
二、实验内容
- 使用拉斯维加斯算法改进基于回溯法的n后问题
- 使用蒙特卡洛算法解决主元素问题
三、问题分析
- 使用拉斯维加斯算法改进基于回溯法的n后问题
在棋盘上相继的各行中随机地放置皇后,并注意使新放置的皇后与已经放置的皇后互不攻击,直至n个皇后均已相容地放置好,或已经没有下一个皇后的可放置位置为止。
2.使用蒙特卡洛算法解决主元素问题
设T[1:n]是一个含有n个元素的数组。当|{i|T[i]=x}|>n/2时,称元素x是数组T的主元素。对于给定的输入数组T,用蒙特卡洛算法判定数组T是否含有主元素。
四、算法描述
1.使用拉斯维加斯算法改进基于回溯法的n后问题
在解决n皇后问题时采用了拉斯维加斯算法和回溯算法相结合的方法,算法设计步骤如下:
①定义了一个Queen类,其中包含Place()函数、Backtrack()函数和QueensLV()函数,分别测试皇后k置于第x[k]列的合法性、回溯法解n后问题和随机放置n个皇后的拉斯维加斯算法。
②主函数中输入皇后数量,初始化Queen类,设置stop值,即停止随机选择位置的次数,然后进行循环,直到找到可以放置皇后的解为止。
③在循环中,首先通过随机放置N个皇后的拉斯维加斯算法找到一个局部可行解,然后再通过回溯法寻找全局最优解。其中,stop参数用来控制拉斯维加斯算法给出可行解所需要的“尝试次数”。
④如果找到了解,则输出解并返回true;如果没有找到,则返回false,继续循环尝试找到解。
2.使用蒙特卡洛算法解决主元素问题
蒙特卡洛算法对随机选择的数组元素x,测试它是否为数组T的主元素。如果算法返回结果为true,这里用1表示,则随机选择的数组元素x是数组T的主元素,如果返回的结果为false,这里用0表示,则数组T可能含有主元素,而随机选择的元素x并不是数组T的主元素。
五、运行结果
1.使用拉斯维加斯算法改进基于回溯法的n后问题
2.使用蒙特卡洛算法解决主元素问题
六、源代码
1.使用拉斯维加斯算法改进基于回溯法的n后问题
#include "RandomNumber.h"
#include <cmath>
#include <iostream>
using namespace std;
class Queen
{
friend bool nQueen(int);
private:
bool Place(int k); //测试皇后k置于第x[k]的合法性
bool Backtrack(int t); //解n后问题的回溯法
bool QueensLV(int stopVegas); //随机放置n个皇后拉斯维加斯算法
int n,*x,*y;
};
//测试皇后k置于第x[k]列的合法性
bool Queen::Place(int k)
{
for(int j=1; j<k; j++)
{
if((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k]))
{
return false;
}
}
return true;
}
//解n后问题的回溯法
bool Queen::Backtrack(int t)//解n后问题的回溯法,回溯尝试皇后位置,t为第t行
{
if (t>n)
{ //放置皇后个数超过n时候 ,可行解个数+1
for (int i=1;i<=n;i++)
y[i]=x[i];
return true;
}
else
for (int i=1;i<=n;i++)
{
x[t]=i; //第t个皇后放在第i列
if (Place(t) && Backtrack(t + 1)) //可以place,就继续放下一个
return true;
}
return false;
}
//随机放置n个皇后拉斯维加斯算法
bool Queen::QueensLV(int stopVegas)//随机放置n个皇后拉斯维加斯算法
{
RandomNumber rnd;
int k=1;
int cnt=1;
while ((k<=stopVegas) && (cnt>0))
{
cnt = 0;
for (int i=1;i<=n;i++)
{
x[k] = i;
if (Place(k))
y[cnt++] = i; //记录下所有可以放置的位置
}
if (cnt>0)
x[k++]=y[rnd.Random(cnt)];//如果能放置,随机选择一个可以放置的位置
}
return (cnt>0);//cnt>0表示放置成功
}
//与回溯法相结合的解n后问题的拉斯维加斯算法
bool nQueen(int n)
{
Queen X;
//初始化X
X.n = n;
int *p = new int[n+1];
int *q = new int[n+1];
for(int i=0; i<=n; i++)
{
p[i] = 0;
q[i] = 0;
}
X.y = p;
X.x = q;
int stop = 3;
if(n>15)
{
stop = n-15;
}
bool found = false;
while(!X.QueensLV(stop));
//算法的回溯搜索部分
if(X.Backtrack(stop+1))
{
for(int i=1; i<=n; i++)
{
cout<<p[i]<<" ";
}
found = true;
cout<<endl;
}
delete []p;
delete []q;
return found;
}
int main()
{
int n;
cout<<"请输入皇后的个数:"<<endl;
cin>>n;
cout<<n<<"皇后问题的解为:"<<endl;
while(!nQueen(n));
return 0;
}
2.使用蒙特卡洛算法解决主元素问题
#include "RandomNumber.h"
#include <cmath>
#include <iostream>
using namespace std;
//判定主元素的蒙特卡罗算法
template<class Type>
bool Majority(Type *T,int n)
{
RandomNumber rnd;
int i=rnd.Random(n);
Type x=T[i]; //随机选择数组元素
int k=0;
for(int j=0;j<n;j++)
{
if(T[j]==x)
{
k++;
}
}
return (k>n/2); //k>n/2时,T含有主元素
}
//重复k次调用蒙特卡洛算法
template<class Type>
bool MajorityMC(Type *T,int n,double e)
{
int k = ceil(log(1/e)/log((float)2));
for(int i=1;i<=k;i++)
{
if(Majority(T,n))
{
return true;//如果存在主元素,则返回true
}
}
return false;//如果数组不存在主元素,则返回false
}
int main()
{
int n;
float e=0.001;
int a[100];
cout<<"请输入数组T的元素个数:";
cin>>n;
cout<<"请依次输入数组T的元素:";
for(int i=0;i<n;i++)
{
cin>>a[i];
}
cout<<"蒙特卡洛算法判断数组T是否含有主元素结果是:"<<MajorityMC(a,n,e)<<endl;
}
两个程序中引用的头文件RandomNumber.h代码如下:
#include <iostream>
#include <ctime>
using namespace std;
//随机数类
const unsigned long maxshort = 65536L;
const unsigned long multiplier = 1194211693L;
const unsigned long adder = 12345L;
class RandomNumber
{
private:
//当前种子
unsigned long randSeed;
public:
RandomNumber(unsigned long s=0);//构造函数,默认值0表示由系统自动产生种子
unsigned short Random(unsigned long n);//产生0:n-1之间的随机整数
double fRandom(void);//产生[0,1)之间的随机实数
};
RandomNumber::RandomNumber(unsigned long s)//产生种子
{
if(s==0)
randSeed = time(0);//用系统时间产生种子
else
randSeed = s;//由用户提供种子
}
unsigned short RandomNumber::Random(unsigned long n)//产生0:n-1之间的随机整数
{
randSeed=multiplier*randSeed+adder;
return (unsigned short)((randSeed>>16) % n);
}
double RandomNumber::fRandom(void)//产生[0,1)之间的随机实数
{
return Random(maxshort) / double(maxshort);
}