随机化算法
随机化算法特征:
对于所求问题的同一实例用同一随机化算法求解两次可能得到完全不同的结果,这两次求解的时间甚至得到的结果可能会有相当大的差别。
分类:
1.数值随机化算法
这类算法常用于数值问题的求解,所得到的解往往都是近似解,而且近似解的精度随计算时间的增加不断提高。
使用该算法的理由是:在许多情况下,待求解的问题在原理上可能就不存在精确解,或者说精确解存在但无法在可行时间内求得,因此用数值随机化算法可以得到相当满意的解。
示例:计算圆周率Π的值
将n个点随机投向一个正方形,设落人此正方形内切圆(半径为r)中的点的数目为k。
假设所投入的点落入正方形的任一点的概率相等,则所投入的点落入圆内的概率为Π*r2/4*r2=Π/4。当n→∞时,k/n→Π/4,从而Π约等于4*k/n。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e2 + 10;
const int INF = 0x3f3f3f3f;
#define m 65536L
#define b 1194211693L
#define c 12345L
class RandomNumber
{
private:
unsigned long d; //d为当前种子
public:
RandomNumber(unsigned long s=0); //默认值0表示由系统自动给种子
unsigned short random(unsigned long n); //产生0:n-1之间的随机整数
double fRandom(void); //产生[0,1)之间的随机实数
};
//函数RandomNumber用来产生随机数种子
RandomNumber::RandomNumber(unsigned long s)
{
if(s==0) d=time(0); //由系统提供随机种子
else d=s; //由用户提供随机种子
}
unsigned short RandomNumber::random(unsigned long n)
{
d=b*d+c; //用线性同余式计算新的种子d
return (unsigned short)((d>>16)%n); //把d的高16位映射到0~(n-1)范围内
}
double RandomNumber::fRandom(void)
{
return random(m)/double(m);
}
double Darts(int n)
{
static RandomNumber darts;
int k=0,i;
double x,y;
for(int i=1;i<=n;++i)
{
x=darts.fRandom();
y=darts.fRandom();
if((x*x+y*y)<=1)
k++;
}
return 4.0*k/double(n);
}
int main()
{
srand(time(NULL));
//指定运行次数,次数越高精度越高
printf("%.8lf\n",Darts(10000));
return 0;
}
int majority(int T[],int n)
{
RandomNumber rnd;
int i=rnd.random(n)+1;
int x=T[i];
//printf("%d %d\n",i,x);
int k=0;
for(int j=1;j<=n;++j)
{
if(T[j]==x)k++;
}
if(k>n/2)
{
return x;
}
else return -INF;
}
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e2 + 10;
const int INF = 0x3f3f3f3f;
#define m 65536L
#define b 1194211693L
#define c 12345L
class RandomNumber
{
private:
unsigned long d; //d为当前种子
public:
RandomNumber(unsigned long s=0); //默认值0表示由系统自动给种子
unsigned short random(unsigned long n); //产生0:n-1之间的随机整数
double fRandom(void); //产生[0,1)之间的随机实数
};
//函数RandomNumber用来产生随机数种子
RandomNumber::RandomNumber(unsigned long s)
{
if(s==0) d=time(0); //由系统提供随机种子
else d=s; //由用户提供随机种子
}
unsigned short RandomNumber::random(unsigned long n)
{
d=b*d+c; //用线性同余式计算新的种子d
return (unsigned short)((d>>16)%n); //把d的高16位映射到0~(n-1)范围内
}
double RandomNumber::fRandom(void)
{
return random(m)/double(m);
}
int T[maxn];
int majority(int T[],int n)
{
RandomNumber rnd;
int i=rnd.random(n)+1;
int x=T[i];
//printf("%d %d\n",i,x);
int k=0;
for(int j=1;j<=n;++j)
{
if(T[j]==x)k++;
}
if(k>n/2)
{
return x;
}
else return -INF;
}
int majorityMC(int T[],int n,double ci)
{
int k=(int)ceil(log(ci)/log(0.5));
printf("%d\n",k);
for(int i=1;i<=k;++i)
{
int tmp=majority(T,n);
if(tmp!=-INF)
return tmp;
}
return -INF;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&T[i]);
}
int tmp=majorityMC(T,n,0.09);
printf("%d\n",tmp);
return 0;
}
偷个懒(嘻嘻
-------------------------------------------------------------------------------------------------------------
一、随机排列数组
随机排列要求对于数组中每一个元素出现到任意一个位置的概率为1/n(数组长度为n)
伪代码:
RANDOMIZE-IN-PLACE ( A , n )
for i ←1 to n
do swap A[i] ↔A[RANDOM(i , n )]
证明:
A[1]位于位置1的概率为1/n,这个显然,因为A[1]不被1到n的元素替换的概率为1/n,而后就不会再改变A[i]了。而A[1]位于位置2的概率也是1/n,因为A[1]要想位于位置2,则必须在第一次与A[k]交换(k=2...n),同时第二次A[2]与A[k]替换,第一次与A[k]交换的概率为(n-1)/n,而第二次替换概率为1/(n-1),所以总的概率是(n-1)/n * 1/(n-1) = 1/n。同理可以推导其他情况。
当然这个条件只能是随机排列数组的一个必要条件,也就是说,满足元素A[i]位于位置j的概率为1/n不一定就能说明这可以产生随机排列数组。因为它可能产生的排列数目少于n!,尽管概率相等,但是排列数目没有达到要求。
但是上面这个算法RANDOMIZE-IN-PLACE可以产生均匀随机排列,证明过程见:https://blog.csdn.net/sgbfblog/article/details/7917685
二、随机选取给定整数流的一个数字
整数流也就是由数字构成的连续序列,这个也要保证整数流每一个位置的数字取到的概率相等
解:
如果数据流长度为1,那么必选第1个数字。
如果数据流长度为2,那么我们选第2个数字的概率为1/2,我们以1/2的概率用第2个数字替换前面选的随机数,得到新的随机数。
.........
如果数据流长度为n,那么我们选择第n个数字的概率为1/n,即我们以1/n的概率用第n个数字替换前面选的随机数,得到新的随机数。
一个简单的方法就是使用随机函数f(n)=bigrand()%n,其中bigrand()返回很大的随机整数,当数据流到第n个数时,如果f(n)==0,则替换前面的已经选的随机数,这样可以保证每个数字被选中的概率都是1/n。如当n=1时,则f(1)=0,则选择第1个数,当n=2时,则第2个数被选中的概率为1/2,以此类推,当数字长度为n时,第n个数字被选中的概率为1/n。
三、随机从n个数里面选取m个数
代码:
void genknuth(int m, int n)
{
for (int i=0; i<n; i++) //必定会选取m个数,当n-i==m的时候,那么对于区间[i,n]每一个数都会被选取
if (bigrand() % (n-i) < m) { //n-i中i每次加1,相当于remaining每次减1
cout << i << endl;
m--; //选取的数目减1
}
}
先考虑个简单的例子,当m=2,n=5时,我们需要从0~4这5个整数中等概率的选取2个有序的整数,且不能重复。如果采用如下条件选取:bigrand() % 5 < 2,则我们选取0的概率为2/5。但是我们不能采取同样的概率来选取1,因为选取了0后,我们应该以1/4的概率来选取1,而在没有选取0的情况下,我们应该以2/4的概率选取1。
-------------------------------------------------------------------------------------------------------------