算法篇-13-随机化-线性同余&主元素问题&N皇后问题&素数测试

本系列所有代码https://github.com/YIWANFENG/Algorithm-github

四中随机化算法

数值随机化算法:

1..这类算法所得到的往往是近似解。

2.近似解的精度随着计算时间的增加而不断提高。

 

蒙特卡罗算法:

1.求问题的准确解,但得到的解不一定正确。

2.计算时间越长,解的正确性越高。

 

拉斯维加斯算法:

1.求正确解,但可能得不到任何解。

2.计算时间越长,得到正确解几率越高。

 

舍伍德算法:

1.总可求的一解,且所求解一定正确。

2.非避免算法最坏,而是消除最坏与特定实例之间的关联。

线性同余产生伪随机数

算法思路分析以及相关数学公式:

X(n+1) = (a * X(n) + c) % m这样的公式,其中:

 模m, m > 0

系数a, 0 < a < m

增量c, 0 <= c < m

原始值(种子) 0 <= X(0) < m

其中参数c, m, a比较敏感,或者说直接影响了伪随机数产生的质量。

由上述公式得出xn+1)后右移16位即得到一个0-65535之间的随机数。

(高16位随机性较低16位好)

#include <iostream>
#include <time.h> 

//线性同余产生伪随机数 
using namespace std;

class CRandom
{
private:
	unsigned long rand_seed; //随机数种子

public:
	CRandom(unsigned long s = 0)
	{
		if(!s) rand_seed = time(0);
		else rand_seed = s; 
	}
	
	unsigned long Random(unsigned long n) 
	{
		//0-n-1之间的随机数 
		const unsigned long multiplier = 1194211693L;
		const unsigned long adder = 15465L;
		
		rand_seed = multiplier*rand_seed + adder;
		
		return (rand_seed>>16) % n;
	} 
	
	double fRandom(void) 
	{
		//产生0-1之间的随机实数 
		const unsigned long large_number =  0x10000;
		return Random(large_number) / double(large_number);
	}
	
};

int main()
{
	CRandom rnd;
	
	int i,n=100000;
	int m[10];
	for(i=0;i<10;++i) m[i]=0;
	//查看生成随机数的分布 
	for(i=0;i<n;i++) 
		m[rnd.Random(1000)/100]++;
	for(i=0;i<10;++i)	
	 	cout<<m[i]/(double)n<<endl;
	cin.get();
	
	return 0;
}

主元素问题

题目:

T[1:n]是一个含有n个元素的数组(集合)。当 | {i | T[i]=x} | > n/2 时,称元素x是数组T的主元素。判断一数组是否含有主元素。

 

算法思路分析以及相关公式:

蒙特卡洛算法,随机从数组中取一个数来测试其是否是该数组的主元素。

一次随机测试很可能出错,在这里我们采用多次取样判断来减小误差。

 

若数组含有主元素,则返回true的概率是p (p>1/2)2次调用返回true的概率是

P+(1-P)*P, = (1-P)^2 > 3/4,如果需要更高准确度则重复更多次数。

#include <time.h> 
#include <stdlib.h>
#include <math.h>

#include <iostream>


//主元素问题-- 蒙特卡洛算法
using namespace std;

template <class T_>
bool Majority(const T_ *t,int n)
{
	srand(time(0));//取消后正确几率下降
	int i = rand()%n;
	T_ x = t[i];
	int k =0;
	for(int j=0;j<n;++j) {
		if(t[j]==x) k++;
	}
	return (k>n/2);
}

template <class T_>
bool MajorityMC(const T_*t,int n,double e)
{
	//e允许的最大错误几率 
	int k =ceil(-log(e)/log(2.0));
	for(int i=1;i<=k;++i) {
		if(Majority(t,n)) return true;
	}
	return false;
}



int main()
{

	srand(time(0));
	int i,n  = 10000;
	int *a = new int[n];
	for(i=0;i<n;++i) a[i] = i+1;
	int counter = 0 ;
	while (counter<n/2+1) {
		i = rand()%n;
		if(a[i]!=0) {
			a[i]=0;
			++counter;
		}
 	}
 	
 	for(i = 0;i<40;++i) {
 		if(MajorityMC(a,n,0.1)) 
 			cout<<true<<endl;
 		else
 			cout<<false<<endl;
    }
    
	cin.get();
	delete []a;
	return 0;
}

N皇后问题

N皇后问题,Las Vegas 算法。

算法分析与相关公式:

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

#include <time.h> 
#include <stdlib.h>
#include <math.h>
#include <iostream>


//nqueue - Las Vegas
 
using namespace std;

class NQueue_Las_Vegas
{
private:
	int *y;  //当前行上皇后可放置的位置
	
	bool Place(int k)		
	{ 
 		//验证第k行皇后位置是否合理 
 		for(int i=1; i<k; ++i) { 
 			if((k-i)==abs(x[i]-x[k]) || x[i]==x[k]) 
 				return false; 
 		} 
 		return true; 
 	} 

	
	bool Backtrack(int t)	
	{
		//回溯法
		if(t>n) return true;
		else {
			for(int i=1;i<=n;++i)
			{
				x[t] = i;
				if(Place(t) && Backtrack(t+1))
					return true;
			}
		}
		return false;
	}
	
	bool QueuesLV(int m)	
	{
		//随机放置m个皇后的Las Vegas算法
		int k = 1;
		int count = 1;
		while(k<=m && 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[rand()%count];
			
		}
		return count>0;
	}
	
public:
	int n;
	int *x;
	
	void Solve(int num_loops)
	{
		y = new int[n+1];
		int m = 5;
		if(n>15) m = n-15;
		bool found = false;
		for(int i=1;i<num_loops;++i) {
			if(QueuesLV(m)) {
				if(Backtrack(m+1)) {
					found = true;
					break;
				}
			}
		}
		if(found) {
			for(int i=1;i<=n;++i) 
				cout<<x[i]<<' ';
			cout<<endl;
		} else {
			cout<<"No Answer"<<endl;
		}
		delete [] y;
	}
};


int main()
{
	NQueue_Las_Vegas nq;
	nq.n = 8;
	nq.x = new int[nq.n+1];
	for(int i=0;i<20;++i)
		nq.Solve(10) ;
		
	cin.get();
	delete [] nq.x;
	return 0;
}

素数测试

 --蒙特卡洛算法  

算法思路分析以及相关公式:

 Wilson定理:对于给定的正整数n,判定n是一个素数的充要条件是(n-1)! -1(mod n)

 费尔马小定理:如果p是一个素数,且0<a<p,则a^(p-1)(mod p)。 

 二次探测定理:如果p是一个素数,且0<x<p,则方程x^21(mod p)的解为x=1p-1

 Carmichael数:费尔马小定理是素数判定的一个必要条件。满足费尔马小定理条件的整数n未必全是素数。有些合数也满足费尔马小定理的条件,这些合数称为Carmichael数。前3Carmichael数是561,11051729Carmichael数是非常少的,在1~100000000的整数中,只有255Carmichael数。

 

#include <time.h> 
#include <stdlib.h>
#include <math.h>
#include <iostream>


//素数测试 --蒙特卡洛算法  
using namespace std;

void PowerAndPrimeTest(unsigned int a,unsigned int p,unsigned int n,
		unsigned int &result,bool &composite)
{
	//计算power(a,p) mod n ,同时实施对n的二次探测 
	//result计算结果		composite是否为合数 
	unsigned int x;
	if(p==0) result = 1;
	else {
		PowerAndPrimeTest(a,p/2,n,x,composite);
		
		result = (x*x)%n;
		if(result==1 && x!=1 && x!=n-1)
			composite = true; 
		//计算结果 
		if(p%2==1) //p是奇数 
			result = (result*a)%n;
	}
	
}

bool PrimeTestMC(unsigned int n,unsigned int k)  
{
	//检测n是否为素数
	//重复调用k次蒙特卡洛算法  
	unsigned int a,result;
	bool composite  = false;
	if(n<5) {
		if(n==2 || n == 3) return true;
		return false;
	} 
	for(int i=1;i<=k;++i) {
		//下面这句决定5以下素数测试有问题 
		a = rand()%(n-3)+2 ;
		PowerAndPrimeTest(a,n-1,n,result,composite);
		if(composite || (result!=1)) return false;
	}
	return true;
}

bool PrimeTest(unsigned int n)   
{
	if(n==1) return false;
	else if(n==2) return true;
	unsigned int i,m = sqrt(n);
	for(i=2;i<=m;++i) 
		if(n%i==0) return false;
	return true;
}

int main()
{
	unsigned int n = 1194211693L;
	for(int i=2000;i<5000;++i) 
		if(PrimeTest(i)) {
			cout<<i<<endl;
			n=i;
			break;
		}
	
	for(int i=0;i<20;++i) {
		if(PrimeTestMC(n,4)) cout<<true<<endl;
		else cout<<false<<endl;
	}
	
	
	cin.get();
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值