算法设计与分析实验报告——实验六 随机化算法

一、实验目的

1. 熟悉随机化算法的基本思想;

2. 使用随机化算法分析、解决问题;

3. 实现算法。

二、实验内容    

  1. 使用拉斯维加斯算法改进基于回溯法的n后问题
  2. 使用蒙特卡洛算法解决主元素问题

三、问题分析 

  1. 使用拉斯维加斯算法改进基于回溯法的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);  
}   

  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值