<1>、拉斯维加斯概念:
拉斯维加斯算法不会得到不正确的解。一旦用拉斯维加斯算法找到一个解,这个解就一定是正确解。但有时用拉斯维加斯算法会找不到解。拉斯维加斯算法找到正确解的概率随着它所用的计算时间的增加而提高。对于所求解的任一实例,用同一拉斯维加斯算法反复对该例求解足够多次,可使求解失效的概率任意小。
通常用一个bool类型的函数表示拉斯维加斯算法,拉斯维加斯算法的典型调用形式为bool success=LV(x,y);当success值为false时,算法未能找到问题的解。此时可对同一实例再次独立地调用相同的算法。
<2>、算法思路:
将拉斯维加斯与回溯法相结合求解n皇后问题,先在棋盘的若干行中随机地放置皇后,然后在后继行中用回溯法继续放置,直到找到一个解或宣告失败。随机放置的皇后越多,后继回溯搜索所需的时间就越少,但失败的概率也就越大。
下表给出用拉斯维加斯回溯算法解八皇后问题时,对于不同的stopVegas值,算法成功的概率p,一次成功搜索访问的结点数平均值s,一次不成功搜索访问的节点数平均值e,以及反复调用算法使得最终找到一个解所访问的节点数的平均值t=s+(1-p)e/p。
其中stopVegas=0相应于完全使用回溯法的情形,由表可以看出来,当n=8时,取stopVegas=2时,效率很高。故本算法取stopVegas=2。
<3>、算法程序:
#include <iostream>
#include <cmath>
#include "RandomNumber.h"
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;
};
bool Queen::Place(int k)
{//测试皇后k置于第x[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;
}
bool Queen::Backtrack(int t)
{//解n后问题的回溯法
if(t>n){
for(int i=1;i<=n;i++)
y[i]=x[i];
return true;
}
else
for(int i=1;i<=n;i++)
{
x[t]=i;
if(Place(t)&&Backtrack(t+1))
return true;
}
return false;
}
bool Queen::QueensLV(int stopVegas)
{//随机放置n个皇后拉斯维加斯算法
RandomNumber rnd;
int k=1;//随机数产生器
int count=1;
//1<=stopVegas<=n表示允许随机放置的皇后数
while((k<=stopVegas)&&(count>0))
{
count=0;
for(int i=1;i<=n;i++)
{
x[k]=i;
if(Place(k))
y[count++]=i;
}
if(count>0)
x[k++]=y[rnd.Random(count)];//随机位置
}
return (count>0);//count>0表示放置成功
}
bool nQueen(int n)
{
//与回溯法相结合的解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=2;
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()
{
if(!nQueen(8))
{
cout<<"搜索不成功!!"<<endl;
}
system("pause");
return 0;
}
其中随机生成头文件RandomNumber.h:
#include <ctime>
//随机数类
const unsigned long maxshort = 65536L;
const unsigned long multiplier = 1194211693L;
const unsigned long adder = 12345L;
class RandomNumber
{
private:
//当前种子
unsigned long randSeed;
public:
RandonNumber(unsigned long s=0);//构造函数,默认值0表示由系统自动产生种子
unsigned short Random(unsigned long n);//产生0:n-1之间的随机整数
double fRandom(void);//产生[0,1)之间的随机实数
};
RandomNumber::RandonNumber(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);
}