离散事件模拟在Windows和Linux系统下产生不同的结果

3
我正在进行一项m/m/2/2的离散事件模拟。结果与理论值相同,这意味着程序是正确的(在Ubuntu的g++环境下)。然而,在Windows中(Visual Studio),只有当试验次数少于10000时才能得到正确的结果。我调试了很长时间,最终发现必须使用(double) rand() / ( RAND_MAX + 1 )而不是(double) rand() / RAND_MAX。为什么会这样?这两个编译器生成的随机数是不同的吗?
#include <iostream>
#include <stdlib.h>
#include <fstream>
#include <cmath>
#include "iomanip"
#include <queue>
using namespace std;

#define TRIAL_NUM 10000000

//structure that represent a customer
struct cust
{
int cust_id;
double arrival_time;
double depart_time;
};

//uniform random generator
double uniRand()
{
    return (double) rand() / RAND_MAX;
}

//exponential random generator
double expRand(double lam)
{
double u,x;
u=uniRand();
x=(-1/lam)*log(1-u);
return(x);
}

int main()
{
    //seed the random generator
srand (time(NULL));                   
//queue that hold all customers
std::queue<cust*> customers;
double lam = 0, mu = 0;
bool showStatus = false;
cout<<"M/M/1/1 Discrete Event Simulation with "<<TRIAL_NUM<<" customers\n"<<" Please spacify the value of lambda:";
cin>>lam;
while(lam<= 0)
{
    cout<<"lambda value must be a positive number, try again:";
    cin>>lam;
}
cout<<"Please specify the value of mu:";
cin>>mu;
while(mu<= 0)
{
    cout<<"lambda value must be a positive number, try again:";
    cin>>mu;
}
cout<<"Show status for each customer? (y/n)";
char show;
cin>>show;
while( tolower(show) != 'y' && tolower(show) != 'n')
{
    cout<<"Invalid input, try again:";
    cin>>show;
}
if ( tolower(show) =='y' )
{
    showStatus = true;
}

//Generating all arrival time, service time for all customers
double temp_time = 1;
cust* temp;
for (int i = 1; i <= TRIAL_NUM; ++i)
{
    temp= new cust;
    temp -> arrival_time = temp_time + expRand(lam);
    temp_time = temp->arrival_time;
    temp->depart_time = temp->arrival_time + expRand(mu);
    temp->cust_id = i;
    customers.push(temp);
}

//Blocking customer count
double block = 0;
bool blockFlag = false;
temp = customers.front();
double server1 = 0, server2 = 0; 

//perform simulation
while( !customers.empty())
{
    if ( server1 < temp->arrival_time)
    {
        server1 = temp->depart_time;
    }
    else if ( server2 < temp->arrival_time)
    {
        server2 = temp->depart_time;
    }
    else
    {
        block++;
        blockFlag = true;
    }
    if (showStatus)
    {
        cout<<"Customer "<<temp->cust_id<<"\tarrived at:"<<temp->arrival_time<<"\tservice time:"<<temp->depart_time<<"\tstatus:";
        if (blockFlag)
        {   
            cout<<"Blocked"<<endl;
        }
        else{
            cout<<"Served"<<endl;
        }
    }
        customers.pop();
        if (!customers.empty())
        {
            temp = customers.front();
        }
        blockFlag = false;
}
cout<<"Blocking probability is: "<<block/TRIAL_NUM<<endl;
return 0;
}

不要使用rand()。在不同的编译器上,它会产生不同的结果,而且结果可能是可怕的。例如,在Windows上打印rand() % 2前20个值,并对输出感到困惑... C++有许多替代方案,std::mt19937可能适合您。 - Andy Newman
1个回答

1

(double) rand() / (RAND_MAX + 1)是一个危险的表达式。

通常情况下,如果RAND_MAX等于INT_MAX,那么RAND_MAX + 1会导致溢出和未定义行为(通常会得到INT_MIN,但这不被任何规范保证)。

在Visual C++中,RAND_MAX为32767,因此您不会遇到这个问题(但32767是一个很小的值,它会很快地“循环”)。

无论如何,我认为真正的问题在于这里:

u = uniRand();
x = (-1 / lam) * log(1 - u);

uniRand()被定义为(double) rand() / RAND_MAX时,它会产生以下范围内的值:
0.0 <= uniRand() <= 1.0

当值等于1.0时,您的log(1-u)中会出现极点误差。由于RAND_MAX的小值,这在使用Visual C++时会更频繁发生。


谢谢,解释非常有帮助!那么我应该怎么做才能使代码在两个平台上都能正常工作,并避免“危险表达式”呢? - codeMonkey
我建议使用(double) rand() / RAND_MAX表达式,并检查x = (-1 / lam) * log(1 - u);以避免域错误(lam1-u接近0.0)。此外,请检查内存分配(您在temp = new cust;中分配了内存,但没有释放它)。 - manlio
另一个可能的解决方案是获取Mersenne Twister的副本,并使用生成器的[0,1)变体。 - pjs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接