用程序给闺女出算数题——我的头脑体操

闺女上一年级,放假了,老师要求假期里每天做20100以内加减法的算术题。我一想,这好几十天,每天出20道,时间长了也够烦的。再说出出来的题,也不一定各种题目都能出到。干脆编个程序,自动出题得了。于是,程序的需求归纳为:

 

随机生成NM以内非负整数加减法的算术题,题目应该在概率上均匀分布。

 

(注:以下C++代码在VS2008上调试运行通过)

 

准备:

为了将分布情况可视化,需要一个分布统计的类DistributionStatistic(见下),来记录2个加数和和,或者被减数、减数和差出现的次数等。每当显示出分布数据时,将其拷到Excel里,让Excel画出分布图。

 

DistributionStatistic类代码

下面开始编写生成算术题的代码。先设置2个常整数M(缺省为100)和N(为了分布统计,得整大点,缺省为10000,见下。

 

常量声明及初始化

 

另外,还要先写个产生[range_min, range_max]范围内随机数的函数Random,以便调用。

 

Random函数代码

做法一:

最直接的想法,随机生成2[0, M]之间的数xy。对于加法,如果其和不超过M,则采用,否则舍弃;对于减法,用大的那个数减去小的那个数。但是按照概率,加法有50%被舍弃,所以出出来的题,加法和减法的比率约为1:2,不满足均匀分布。所以对于减法,当第一个数x小于第二个数y的时候,也舍弃。这样可以达到概率上加法和减法比率为1:1。代码见下。

 

做法一代码

以加法为例,2个加数和和的分布情况如下(横坐标为题目中的数值0-100,纵坐标为对应数值出现的次数)。

 

image

 

虽然能解决问题,但毕竟做法一有舍弃50%的情况,效率低。所以改进成做法二。

 

做法二:

对于加法,第一个数x生成范围不变(仍是[0, M]),第二个数y随机生成的范围变为[0, M-x],以保证xy的和不会超过M(这样就不会有舍弃的情况);而对于减法,第二个数y随机生成的范围变为[0, x],以保证xy的差不会是负数(这样也不会有舍弃的情况)。代码见下。

 

做法二代码

但是题目分布情况好像有点问题,跟做法一不同,做法二加法的分布图如下。

 

image

 

直观地想想,和为100的题目包括:0+1001+99……100+0,共101种;和为99的共100种,和为98的共99种,以此类推,和为0的为1种。所以如果题目均匀分布的时候,和的分布数据应该是等差的,即是线性变化的,不应该画出这种非线形的图形。造成这种非线形分布曲线的原因,就是由于第二个数以第一个数为基础生成的。看来前2种做法各有缺点,得再找出一种即没有舍弃的情况,又能均匀分布的做法。

 

做法三:

假设M=100,所有M以内非负整数加法的题目见如下三角形表格:

 

100+0

99+0

99+1

98+0

98+1

98+2

2+0

2+98

1+0

1+1

1+98

1+99

0+0

0+1

0+2

0+98

0+99

0+100

 

它们的总个数应该是:

image

所以,基本的思路是:为每道题设置一个索引值(比如0+0索引为00+1索引为10+100索引为1001+0索引为101,以此类推),然后随机生成一个[0, S-1]随机数r,将其作为索引值,找到其对应的题目。

 

从索引值r转换到对应题目的2个加数(xy),稍微有点麻烦。转换公式为:

image

约束条件为:

image

所以在代码实现中,x0循环到M,每次计算出y,一旦y满足约束条件,则x+y即为r对应的题目。

 

减法与加法类似,转换公式为:

image

约束条件为:

image

在代码中,x0循环到M,每次计算出y,如果y满足约束条件,则x-y即为r对应的题目。整个做法三的代码如下。

 

做法三代码

此做法的分布图如下。

 

image

 

此做法虽然解决了前2种做法的问题,但是它又出现一个新问题,即x的循环造成生成每道题的时间复杂度从O(1)变为O(M)。所以效率上还需要提高,于是基于这一做法,进行一些改进,实现做法四。

 

做法四:

将做法三的加法题目的三角形表格和减法题目的三角形表格相扣,生成一个矩形表格,见下。

 

100+0

100-100

100-99

100-98

100-2

100-1

100-0

99+0

99+1

99-99

99-98

99-1

99-0

98+0

98+1

98+2

98-98

98-0

2+0

2+98

2-2

2-1

2-0

1+0

1+1

1+98

1+99

1-1

1-0

0+0

0+1

0+2

0+98

0+99

0+100

0-0

 

题目的总个数是(假设M=100):

image

这时,从[0, S-1]随机数r转换为xy和加减运算符就比较简单了(直接计算,无需循环)。如下:

x = r / (M+2)

y1 = r % (M+2)

如果x+y1<=M,则y=y1,题目为x+y;否则y=M+1-y1,题目为x-y。代码如下。

 

做法四代码

做法四解决了前三种做法的几个问题,应该说无论从均匀分布上,还是效率上,都是最好的。

 

这个问题应该说比较简单,用到的只是些中学数学的知识。本人一直使用“做法”,而避免使用“算法”这个词,为的是避免自己觉得像是在作算法研究。赋闲在家,头脑不免变得迟钝,为了避免人们所说的“头脑动脉硬化症”,经常思考思考生活中的小问题,保持头脑灵活,不让其“生锈”。这不,给闺女出题,变成了我的头脑体操。

 

posted @ 2011-06-07 13:51  wanghui  阅读(5383)  评论(49编辑  收藏  举报