代码改变世界

记录:随机数生成的一个bug分析

2018-03-04 17:01  mengmz  阅读(615)  评论(0编辑  收藏  举报

开门见山,直接上代码:

std::wstring arithmetic_expression::natrual_number_or_fraction_generator()
{
	static std::default_random_engine e;
	static std::uniform_int_distribution<unsigned> natural_fraction(0, 1);
	static std::uniform_int_distribution<unsigned> natural(1, number_range - 1);
	
	std::wstring result;
	if (natural_fraction(e) == 0)
	{
		result =  MyFraction(natural(e), 1).to_wstring();
	}
	else if (natural_fraction(e) ==  1)
	{
		unsigned a = natural(e);
		unsigned b = natural(e);
		result =  MyFraction(a, b).to_wstring();
	}
	return result;
}

上述函数源自于一个生成四则运算的小程序,其功能是在产生四则运算的数字时,随机的产生自然数 或者真分数 ,但在测试时出现了奇怪的输出:

可以看到在箭头位置部分的数值没有正确的生成。

为了解决这个问题,我将代码改成了如下的形式,增加一个中间变量储存随机数生成器的值。

std::wstring arithmetic_expression::natrual_number_or_fraction_generator()
{

	static std::default_random_engine e;
	static std::uniform_int_distribution<unsigned> natural_fraction(0, 1);
	static std::uniform_int_distribution<unsigned> natural(1, number_range - 1);
	
	std::wstring result;
	unsigned choice = natural_fraction(e);
	if (choice == 0)  //改变
	{
	result = MyFraction(natural(e), 1).to_wstring();
	}
	else if (choice == 1)
	{
	unsigned a = natural(e);
	unsigned b = natural(e);
	result = MyFraction(a, b).to_wstring();
	}
	return result;
}

经过修改后可以正常的生成表达式了,问题得到了解决,但原因是什么呢?思考了一中午后,终于发现了关键所在:

  • 每次调用随机数分布函数 时,都会生成一个新的随机数,这是它的语法特征;

  • 我建立的0-1分布,只有两种结果,0 或者1;

  • 在第一种写法中,if...else 判断中的条件语句每次都调用了随机数分布函数,那么可能存在如下的情况

    • if (natural_fraction(e) == 0) 时,natural_fraction(e) 的值为1,由于条件判断为假,跳到else if
    • 此时,继续调用natural_fraction(e) ,于是生成了新的随机数, 其值可能为0,这样的话else if (natural_fraction(e) == 1) 也为假,奇怪现象就此产生 ,返回值变成了空值。

    事实是否就是这样的呢?在debug模式下观察出错的调用部分:

可以看到,与先前推断的一致。至此,终于揭开了这个bug的背后原因,同时也暴露了我对随机数使用的经验缺乏,掉进了这么个里。

特此记录!