1. 程式人生 > 實用技巧 >拉斯維加斯演算法之n後問題

拉斯維加斯演算法之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