拉斯维加斯算法之n后问题

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

     舍伍德算法优点在于计算时间复杂度对所有实例相对均匀,但与其相应的确定性算法相比,其平均时间复杂度没有改进。拉斯维加斯算法则不然,它能显著改进算法的有效性,甚至对某些迄今为止找不到有效算法的问题,也能得到满意的算法。

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

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

设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个皇后均已相容地放置好,或已没有下一个皇后的可放置位置时为止。

      具体实现代码如下:

//随机化算法 拉斯维加斯算法 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;  
}
View Code
#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);
}
View Code

代码实现结果:

 上述算法一旦发现无法再放置下一个皇后,就全部重新开始,下面是将随机放置策略与回溯法结合例子。

//随机化算法 拉斯维加斯算法 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;  
}
View Code
#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);
}
View Code

运行结果:

 参考文献:王晓东《算法设计与分析》第二版

                   https://blog.csdn.net/liufeng_king/article/details/9245585

原文地址:https://www.cnblogs.com/cy0628/p/14010229.html