如何确定rand生成的0到1之间的随机数不为0?

时间:2019-05-10 19:51:35

标签: c++ random

我正在模拟一个排队模型,我需要使用此算法生成我的到达信息。但是,我有时获得0.000000作为随机值R1,并且无法计算ln(0),因此过程停止。如何避免得到0?

我考虑过给每个添加0.0000001,但这也不错,因为我可能会得到大于1的值?

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

main()
    {
        int a=3, b=4, T=500, I=0, l=1504;
        srand(time(NULL));
        float R1, R2, t=0, S[10000], f;

        printf("Utilizaremos la funcion at+b para el simular el poceso\n");
        while(t<T){
            R1=rand()/(double)RAND_MAX;
            t= t-log(R1)/l;
            R2=rand()/(double)RAND_MAX;
            f = a*t+b;
            if(R2<f/l){
                I=I+1;
                S[I]=t;
                printf(" \n I %d, R1 %f, R2 %f, t %f, S[i] %f", I,R1,R2,t,S[I]);
            }


        }
        getche();
        return 0;
}

6 个答案:

答案 0 :(得分:3)

不是rand()是一件好事,但有效地避免零是容易的:

double nzrand() {
  constexpr double max=RAND_MAX+1.;
  return (rand()+1.)/max;
}

这可以恰好返回1;当然,请使用+2.来避免这种情况。

当然,更常见的方法是在[0,1)上生成随机值(此处,通过将1加到分母而不是 分子上)并计算log(1-x)

答案 1 :(得分:2)

只需获得一个随机数,直到您不获得0

double myrand()
{
    while(1)
    {
        double r = rand()/(double)RAND_MAX;
        if (r != 0)
            return r;
    }
}

...

R1 = myrand();  // Will not be 0

答案 2 :(得分:2)

C ++中更好的新方法是使用C ++ 11标准库的新random number generator functionality

使用uniform_real_distribution,您可以将范围指定为double值,例如(1e-12, 1.0)。或者更好的方法是使用(std::numeric_limits<double>::min(), 1.0),它会为您提供最小的double值,该值不为零,但仍归一化(2.122e-314)。

完整示例:

#include <stdio.h>
#include <math.h>
#include <random>

int main()
{
    int a = 3, b = 4, T = 500, I = 0, l = 1504;
    std::random_device rndsource;
    std::minstd_rand rndgen(rndsource());
    std::uniform_real_distribution<double> dist(1e-12, 1.0);
    double R1, R2, t = 0, S[10000], f;

    printf("Utilizaremos la funcion at+b para el simular el poceso\n");
    while (t < T) {
        R1 = dist(rndgen);
        t = t - log(R1) / l;
        R2 = dist(rndgen);
        f = a * t + b;
        if (R2 < f / l) {
            I = I + 1;
            S[I] = t;
            printf(" I %d, R1 %f, R2 %f, t %f, S[i] %f\n", I, R1, R2, t, S[I]);
        }
    }
    return 0;
}

std::minstd_rand是一个LCG,它非常快并且可以在大多数科学应用中使用。为了获得更好的不可预测性,请尝试使用std::mt19937 PRNG。

答案 3 :(得分:1)

我可以想到两种简单的方法。

  1. 在[0,1)中生成一个数字并从1中减去

    R1 = rand() / (RAND_MAX + 1.0);
    R1 = 1.0 - R1;
    
  2. 将rand()的结果加1并相应增加除数:

    R1 = (rand() + 1.0) / (RAND_MAX + 1.0);
    

两者都导致(0,1]中的数字。

答案 4 :(得分:1)

好的,这是正确的答案。

  • 首先,您应该使用C ++ 11工具(或者,如果使用的是C ++ 03,则使用类似的BOOST随机库)。
  • 第二,默认的双随机数生成需要至少53个尾数的随机位,因此最好使用64位的位流生成器(以下示例中为mt19937_64)。因此,一次致电rng会产生一倍的收益,并且在64位平台上通常会更快。
  • 第三,默认的统一双精度生成器生成的数字在严格的[0 ... 1)范围内。使用1-R技巧将其推入(0 ... 1]范围。
  • 第四,您的代码有一个错误-容易使数组S大小溢出而导致内存损坏,因此我添加了适当的防护措施。
  • 最后-请不要按照其他人的建议使用random_device。通常,您需要完全可重复的运行,因此可以更好地控制种子。

代码,Visual C ++ 2019,Win10 x64

#include <cmath>
#include <random>
#include <cstdio>

int main() {
    int a = 3, b = 4, T = 500, I = 0, l = 1504;

    double R1, R2, t = 0, S[10000], f;

    std::mt19937_64 rng{ 7719716254321ULL }; // init with known seed - reproducability
                                             // no need in random device
    std::uniform_real_distribution<double> rnd{};

    printf("Utilizaremos la funcion at+b para el simular el poceso\n");
    while (t < T) {
        R1 = rnd(rng);
        t = t - log(1.0 - R1) / l;
        R2 = rnd(rng);
        f = a * t + b;
        if (R2 < f / l) {
            S[I] = t;
            printf(" I %d, R1 %f, R2 %f, t %f, S[i] %f\n", I, R1, R2, t, S[I]);
            ++I;
            if (I == sizeof(S) / sizeof(S[0]))
                break;
        }
    }
    (void)getchar();

    return 0;
}

答案 5 :(得分:0)

还有另一种可能性:

double increment = 0.000000001;  // However small you need.
double near_zero = 0.005;
...

double nzrand() {

  double nzr = rand()/(double)RAND_MAX;
  if (nzr < near_zero) {
    nzr += increment;
  }
  return nzr;

} // end nzrand()

这仅在初始值远小于1.0时才增加较小的增量,因此它永远不会超过1。它永远不会返回零,最小的返回值就是给定的增量。