我正在模拟一个排队模型,我需要使用此算法生成我的到达信息。但是,我有时获得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;
}
答案 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)
我可以想到两种简单的方法。
在[0,1)中生成一个数字并从1中减去
R1 = rand() / (RAND_MAX + 1.0);
R1 = 1.0 - R1;
将rand()的结果加1并相应增加除数:
R1 = (rand() + 1.0) / (RAND_MAX + 1.0);
两者都导致(0,1]中的数字。
答案 4 :(得分:1)
好的,这是正确的答案。
代码,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。它永远不会返回零,最小的返回值就是给定的增量。