0047算法笔记——【随机化算法】拉斯维加斯(Las Vegas)算法和n后问题

      1、拉斯维加斯(Las Vegas)算法

     拉斯维加斯算法不会得到不正确的解。一旦用拉斯维加斯算法找到一个解,这个解就一定是正确解。但有时用拉斯维加斯算法找不到解。与蒙特卡罗算法类似,拉斯维加斯算法找到正确解的概率随着它所用的计算时间的增加而提高。对于所求解问题的任一实例,用同一拉斯维加斯算法反复对该实例求解足够多次,可使求解失败的概率任意小。拉斯维加斯算法的一个显著特征是它所作的随机性决策有可能导致算法找不到所需的解。

void obstinate(Object x, Object y)
{// 反复调用拉斯维加斯算法LV(x,y),直到找到问题的一个解y
    bool success= false;
    while (!success) success=lv(x,y);
}

      设p(x)是对输入x调用拉斯维加斯算法获得问题的一个解的概率。一个正确的拉斯维加斯算法应该对所有输入x均有p(x)>0。设t(x)是算法obstinate找到具体实例x的一个解所需的平均时间 ,s(x)和e(x)分别是算法对于具体实例x求解成功或求解失败所需的平均时间,则有。解此方程得:


     2、n后问题

     问题描速:在n×n格的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于在n×n格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。


     1)纯拉斯维加斯随机算法求解思路

      对于n后问题的任何一个解而言,每一个皇后在棋盘上的位置无任何规律,不具有系统性,而更象是随机放置的。在棋盘上相继的各行中随机地放置皇后,并注意使新放置的皇后与已放置的皇后互不攻击,直至n个皇后均已相容地放置好,或已没有下一个皇后的可放置位置时为止。

      具体实现代码如下:

     1、RandomNumber.h

#include"time.h"
//随机数类
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);
}
     2、7d4d1.cpp

//随机化算法 拉斯维加斯算法 n后问题
#include "stdafx.h"
#include "RandomNumber.h"
#include <cmath>
#include <iostream>
using namespace std;

class Queen
{
	friend void nQueen(int);
	private:
		bool Place(int k);		//测试皇后k置于第x[k]列的合法性
		bool QueensLv(void);	//随机放置n个皇后拉斯维加斯算法
		int n;					//皇后个数
		int *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::QueensLv(void)
{
	RandomNumber rnd;		//随机数产生器
	int k = 1;				//下一个皇后的编号
	int count = 1;			//在一列中,可以放置皇后的个数

	while((k<=n)&&(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 表示放置成功
}

//解n后问题的拉斯维加斯算法
void nQueen(int n)
{
	Queen X;
	X.n = n;

	int *p = new int[n+1];
	for(int i=0; i<=n; i++)
	{
		p[i] = 0;
	}
	X.x = p;
	X.y = new int[n+1];

	//反复调用随机放置n个皇后的拉斯维加斯算法,直到放置成功
	while(!X.QueensLv());

	for(int i=1; i<=n; i++)
	{
		cout<<p[i]<<" ";
	}
	cout<<endl;
	delete []p;
}

int main()
{
	int n=8;  
    cout<<n<<"皇后问题的解为:"<<endl;  
    nQueen(n);  
    return 0;  
}
     程序运行结果如下:


     2)与回溯法结合的拉斯维加斯随机算法求解思路

     如果将上述随机放置策略与回溯法相结合,可能会获得更好的效果。可以先在棋盘的若干行中随机地放置皇后,然后在后继行中用回溯法继续放置,直至找到一个解或宣告失败。随机放置的皇后越多,后继回溯搜索所需的时间就越少,但失败的概率也就越大。 

  算法具体代码如下:

    1、RandomNumber.h如上

    2、7d4d1-2.cpp

//随机化算法 拉斯维加斯算法 n后问题
#include "stdafx.h"
#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)
{
	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;
}


//随机放置n个皇后拉斯维加斯算法
bool Queen::QueensLV(int stopVegas)
{
	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表示放置成功
}

//与回溯法相结合的解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=8;  
    cout<<n<<"皇后问题的解为:"<<endl;  
    while(!nQueen(n));  
    return 0;  
}
     程序运行结果如图:



阅读更多
换一批

没有更多推荐了,返回首页