⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pseudorandom.cpp

📁 随机数类,产生随机数
💻 CPP
📖 第 1 页 / 共 3 页
字号:
 */
double PseudoRandom::fdist(const double nu1, const double nu2)
{

	double Y1 =  gamma (nu1 / 2, 2.0);
	double Y2 =  gamma (nu2 / 2, 2.0);

	double f = (Y1 * nu2) / (Y2 * nu1);

	return f;
}

/**** The T Distribution ****/
/* The t-distribution arises in statistics. 
 * If Y_1 has a normal distribution and Y_2 has a chi-squared distribution with \nu degrees of freedom then the ratio,
 * X = { Y_1 \over \sqrt{Y_2 / \nu} }
 * has a t-distribution t(x;\nu) with \nu degrees of freedom. 
 * The t-distribution has the form
 * p(x) dx = (Gamma((nu + 1)/2)/(sqrt(pi nu) Gamma(nu/2)) * (1 + (x^2)/nu)^-((nu + 1)/2) dx
 * The method used here is the one described in Knuth 
 */
double PseudoRandom::tdist (const double nu)
{
	if (nu <= 2)
    {
		double Y1 = gaussian (0, 1.0);
		double Y2 = chisq (nu);

		double t = Y1 / sqrt (Y2 / nu);

		return t;
    }
	else
    {
		double Y1, Y2, Z, t;
		do
        {
			Y1 = gaussian (0, 1.0);
			Y2 = exponential (1 / (nu/2 - 1));

			Z = Y1 * Y1 / (nu - 2);
        }
		while (1 - Z < 0 || exp (-Y2 - Z) > (1 - Z));

      /* Note that there is a typo in Knuth's formula, the line below
       * is taken from the original paper of Marsaglia, Mathematics of
       * Computation, 34 (1980), p 234-256 
       */

		t = Y1 / sqrt ((1 - 2 / nu) * (1 - Z));
		return t;
    }
}

/**** The Beta Distribution ****/
/* The beta distribution has the form
 * p(x) dx = (Gamma(a + b)/(Gamma(a) Gamma(b))) x^(a-1) (1-x)^(b-1) dx
 * The method used here is the one described in Knuth 
 */
double PseudoRandom::beta(const double a, const double b)
{
	double x1 = gamma (a, 1.0);
	double x2 = gamma (b, 1.0);
	
	return x1 / (x1 + x2);
}

/**** The Logistic Distribution ****/
/* The logistic distribution has the form,
 * p(x) dx = (1/a) exp(-x/a) / (1 + exp(-x/a))^2 dx
 * for -infty < x < infty 
 */
double PseudoRandom::logistic(const double a)
{
	double x, z;

	do
    {
		x = randDblExc();
    }
	while (x == 1);

	z = log (x / (1 - x));

	return a * z;
}

/**** The Pareto Distribution ****/
/* The Pareto distribution has the form,
 * p(x) dx = (a/b) / (x/b)^(a+1) dx     for x >= b
 */
double PseudoRandom::pareto(double a, const double b)
{
	double x = randDblExc();

	double z = pow (x, -1 / a);

	return b * z;
}

/**** The Weibull Distribution ****/
/* The Weibull distribution has the form,
 * p(x) dx = (b/a) (x/a)^(b-1) exp(-(x/a)^b) dx
 */
double PseudoRandom::weibull(const double a, const double b)
{
	double x = randDblExc();

	double z = pow (-log (x), 1 / b);

	return a * z;
}

/**** The Type I Gumbel Distribution ****/
/* The Type I Gumbel distribution has the form,
 * p(x) dx = a b exp(-(b exp(-ax) + ax)) dx
 * for -\infty < x < \infty.
 */
double PseudoRandom::gumbel1(const double a, const double b)
{
	double x = randDblExc();

	double z = (log(b) - log(-log(x))) / a;

	return z;
}

/**** The Type II Gumbel Distribution ****/
/* The Type II Gumbel distribution has the form,
 * p(x) dx = b a x^-(a+1) exp(-b x^-a)) dx
 * for 0 < x < \infty.
 */
double PseudoRandom::gumbel2(const double a, const double b)
{
	double x = randDblExc();

	double z = pow(-b / log(x), 1/a);

	return z;
}

/**** The Dirichlet Probability Distribution ****/
/* The Dirichlet probability distribution of order K-1 is 
 * p(\theta_1,...,\theta_K) d\theta_1 ... d\theta_K = (1/Z) \prod_i=1,K \theta_i^{alpha_i - 1} \delta(1 -\sum_i=1,K \theta_i)
 * The normalization factor Z can be expressed in terms of gamma functions:
 * Z = {\prod_i=1,K \Gamma(\alpha_i)} / {\Gamma( \sum_i=1,K \alpha_i)}  
 * The K constants, \alpha_1,...,\alpha_K, must be positive. The K parameters, 
 * \theta_1,...,\theta_K are nonnegative and sum to 1.
 * The random variates are generated by sampling K values from gamma
 * distributions with parameters a=\alpha_i, b=1, and renormalizing. 
 * See A.M. Law, W.D. Kelton, Simulation Modeling and Analysis (1991).
 * Gavin E. Crooks <gec@compbio.berkeley.edu> (2002)
 */
void PseudoRandom::dirichlet(const unsigned K, const double alpha[], double theta[])
{
	unsigned int i;
	double norm = 0.0;

	for (i = 0; i < K; i++)
    {
		theta[i] = gamma (alpha[i], 1.0);
    }

	for (i = 0; i < K; i++)
    {
		norm += theta[i];
    }

	for (i = 0; i < K; i++)
    {
		theta[i] /= norm;
    }
}

/**** The Poisson Distribution ****/
/* The poisson distribution has the form
 * p(n) = (mu^n / n!) exp(-mu) 
 * for n = 0, 1, 2, ... . 
 * The method used here is the one from Knuth.
 */
unsigned int PseudoRandom::poisson(double mu)
{
	double emu;
  	double prod = 1.0;
  	unsigned int k = 0;
  	
  	while (mu > 10)
    {
    	unsigned int m = mu * (7.0 / 8.0);

      	double X = gamma_int (m);
      	
      	if (X >= mu)
        {
        	return k + binomial (mu / X, m - 1);
        }
        else
        {
        	k += m;
          	mu -= X; 
        }
    }

  // This following method works well when mu is smal

  	emu = exp (-mu);

  	do
  	{
  		prod *= randExc();
      	k++;
    }
    while (prod > emu);
    
    return k - 1;
}

/**** The Bernoulli Distribution ****/
/* The bernoulli distribution has the form,
 * prob(0) = 1-p, prob(1) = p
 */
unsigned int PseudoRandom::bernoulli(double p)
{
	double u = randExc();
	
	if (u < p)
	{
		return 1 ;
	}
	else
	{
		return 0 ;
	}
}

/**** The Binomial Distribution ****/
/* The binomial distribution has the form,
 * prob(k) =  n!/(k!(n-k)!) *  p^k (1-p)^(n-k) for k = 0, 1, ..., n
 * This is the algorithm from Knuth 
 */
unsigned int PseudoRandom::binomial(double p, unsigned int n)
{
	unsigned int i, a, b, k = 0;
	
	while (n > 10)        // This parameter is tunable 
	{
		double X;
		a = 1 + (n / 2);
		b = 1 + n - a;
		
		X = beta ((double) a, (double) b);
		
		if (X >= p)
		{
			n = a - 1;
			p /= X;
		}
		else
		{
			k += a;
			n = b - 1;
			p = (p - X) / (1 - X);
		}
	}
	
	for (i = 0; i < n; i++)
	{
		double u = randExc();
		if (u < p)
			{
				k++;
			}
	}
	
	return k;
}

/**** The Multinomial Distribution ****/
/* The multinomial distribution has the form
 *
 *                                    N!           n_1  n_2      n_K
 * prob(n_1, n_2, ... n_K) = -------------------- p_1  p_2  ... p_K
 *                           (n_1! n_2! ... n_K!) 
 *
 * where n_1, n_2, ... n_K are nonnegative integers, sum_{k=1,K} n_k = N,
 * and p = (p_1, p_2, ..., p_K) is a probability distribution. 
 * Random variates are generated using the conditional binomial method.
 * This scales well with N and does not require a setup step.
 * Ref: 
 * C.S. David, The computer generation of multinomial random variates,
 * Comp. Stat. Data Anal. 16 (1993) 205-217
 */
void PseudoRandom::multinomial(const unsigned int K, const unsigned int n, const double p[], unsigned int d[])
{
	unsigned int i;
	double norm = 0.0;
	double sum_p = 0.0;

	unsigned int sum_n = 0;

  /* p[k] may contain non-negative weights that do not sum to 1.0.
   * Even a probability distribution will not exactly sum to 1.0
   * due to rounding errors. 
   */

	for (i = 0; i < K; i++)
    {
		norm += p[i];
    }

	for (i = 0; i < K; i++)
    {
		if (p[i] > 0.0)
        {
			d[i] = binomial (p[i] / (norm - sum_p), n - sum_n);
        }
		else
        {
			d[i] = 0;
        }
		sum_p += p[i];
		sum_n += d[i];
    }

}
/**** The Negative Binomial Distribution ****/
/* The negative binomial distribution has the form,
 * prob(k) =  Gamma(n + k)/(Gamma(n) Gamma(k + 1))  p^n (1-p)^k 
 * for k = 0, 1, ... . Note that n does not have to be an integer.
 * This is the Leger's algorithm (given in the answers in Knuth) 
 */
unsigned int PseudoRandom::negative_binomial(double p, double n)
{
	double X = gamma (n, 1.0) ;
	unsigned int k = poisson (X*(1-p)/p) ;
	return k ;
}

/**** The Pascal Distribution ****/
/* The Pascal distribution is a negative binomial with valued integer n
 * prob(k) =  (n - 1 + k)!/(n!(k - 1)!) *  p^n (1-p)^k for k = 0, 1, ..., n
 */
unsigned int PseudoRandom::pascal_(double p, unsigned int n)
{
	/* This is a separate interface for the pascal distribution so that
	 * it can be optimized differently from the negative binomial in future.
	 * e.g. if n < 10 it might be faster to generate the Pascal
	 * distributions as the sum of geometric variates directly.  
	 */
     
	unsigned int k = negative_binomial (p, (double) n);
  	return k;
}

/**** The Geometric Distribution ****/
/* Geometric distribution (bernoulli trial with probability p) 
 * prob(k) =  p (1 - p)^(k-1) for n = 1, 2, 3, ...
 * It gives the distribution of "waiting times" for an event that occurs with probability p. 
 */
unsigned int PseudoRandom::geometric(const double p)
{
	double u = randDblExc();
	
	unsigned int k;
	
	if (p == 1)
	{
		k = 1;
	}
	else
	{
		k = log (u) / log (1 - p) + 1;
	}
	
	return k;
}

/**** The Hypergeometric Distribution ****/
/* The hypergeometric distribution has the form,
 * prob(k) =  choose(n1,t) choose(n2, t-k) / choose(n1+n2,t)
 * where choose(a,b) = a!/(b!(a-b)!) 
 * n1 + n2 is the total population (tagged plus untagged)
 * n1 is the tagged population
 * t is the number of samples taken (without replacement)
 * k is the number of tagged samples found
 */

unsigned int PseudoRandom::hypergeometric(unsigned int n1, unsigned int n2, unsigned int t)
{
	const unsigned int n = n1 + n2;

	unsigned int i = 0;
	unsigned int a = n1;
	unsigned int b = n1 + n2;
	unsigned int k = 0;

	if (t > n)
    {
		t = n ;
    }

	if (t < n / 2) 
    {
		for (i = 0 ; i < t ; i++)
        {
			double u = randExc();
          
			if (b * u < a)
            {
				k++ ;
				if (k == n1)
					return k ;
              a-- ;
            }
			b-- ;
        }
		return k;
    }
	else
    {
		for (i = 0 ; i < n - t ; i++)
        {
			double u = randExc();
          
			if (b * u < a)
            {
				k++ ;
				if (k == n1)
					return n1 - k ;
				a-- ;
            }
			b-- ;
        }
		return n1 - k;
    }
}

/**** The Logarithmic Distribution ****/
/* Logarithmic distribution 
 * prob(n) =   p^n / (n log(1/(1-p)) for n = 1, 2, 3, ...
 * We use Kemp's second accelerated generator, from Luc Devroye's book
 * on "Non-Uniform Random Variate Generation", Springer 
 */
unsigned int PseudoRandom::logarithmic(const double p)
{
	double c = log (1-p) ;

	double v = randDblExc();
  
	if (v >= p)
    {
		return 1 ;
    }
	else
    {
		double u = randDblExc();      
		double q = 1 - exp (c * u);

		if (v <= q*q)
        {
			double x = 1 + log(v)/log(q) ;
			return x ;
        }
		else if (v <= q)
        {
			return 2;
        }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -