用列表法分析1500年前南北朝时期的“奥数”题

偶然发现一道小学奥数题,题目如下:

老师给全班同学排队,每3个人站一排,最后一排只有2个人;每5个人站一排,最后一排为3个人;每7个人站一排,最后一排为2个人,请问全班最少多少名同学?

此题看似一般,但经查阅,却极有渊源,原来题目源于《孙子算经》,此书著于南北朝时期,作者不详,与春秋时期《孙子兵法》的作者并非一人。

目录

一、《孙子算经》之“物不知数”

1、原文及译文

2、计算公式

3、剩余定理

二、规律探索

1、《孙子歌诀》

2、表格解析

(1)最小公倍数105的解析

(2)除7的余数对应系数15的解析

(3)除5的余数对应系数21的解析

(4)除3的余数对应系数70的解析

(5)《孙子算经》“不知其数”结果分析

三、为什么要研究算法

1、效率最低的算法

2、高级一点的算法

3、孙子算经算法


一、《孙子算经》之“物不知数”

1、原文及译文

《孙子算经》卷下第二十六题“物不知数”原文如下:

今有物,不知其数。三三数之,剩二;五五数之,剩三;七七数之,剩二。问:物几 何?

答曰:二十三。

术曰:三三数之,剩二,置一百四十;五五数之,剩三,置六十三;七七数之,剩二 ,置三十。并之,得二百三十三,以二百一十减之,即得。凡三三数之,剩一,则置七十 ;五五数之,剩一,则置二十一;七七数之,剩一,则置十五。一百六以上,以一百五 减之,即得。

“物不知数”的译文如下:

现有一些物品,具体数目不详。3个3个数,最后剩2个;5个5个数,最后剩3个;7个7个数,最后剩2个,问这些物品多少个?

答案:23个

解题思路:3个3个数,余数是2,余数乘以70等于140;5个5个数,余数是3,余数乘以21等于63;7个7个数,余数2,余数乘以15等于30。把以上三个得数加起来就是140+63+30=233。这个数大于3*5*7的最小公倍数105,所以要减去若干次105直到小于105为止,经过减两次105共210,得出答案:233-210=23。解题规律:3个3个数,用其余数乘以70;5个5个数,用其余数乘以21;7个7个数,用其余数乘以15;再把三个得数相加起来,如果得数大于等于106,那么就减去若干次105,直到小于105,就是正确答案。

2、计算公式

根据孙子算经得出计算得公式为:被除数=除3余数×70+除5余数×21+除7余数×15,如果大于105,则减若干次105,直至小于105时为止,得到的那个数就是正确答案。

注意:“不知其数”原文中省略了是符合条件的最小数,本文以下介绍的内容,也默认是符合条件的最小数。

3、剩余定理

“物不知数”文中不但给出了题目、答案,还给出了解题的技巧和规律。宋朝数学家秦九韶于1247年《数书九章》卷一、二《大衍类》对“物不知数”问题做出了完整系统的解答,进一步开创了对一次同余式理论的研究工作,推广了“”。德国数学家高斯于公元1801年出版的《算术探究》中明确地写出了上述定理。公元1852年,英国基督教士伟烈亚士将《孙子算经》“物不知数”问题的解法传到欧洲,公元1874年马蒂生[L.Mathiesen]指出孙子的解法符合高斯的定理,从而在西方的数学史里将这一个定理称为“中国的剩余定理”[Chinese remainder theorem]。

下图是用现代数学语言来解决“物不知数”问题的解题公式:

二、规律探索

利用现代数学方法,可以推导出此公式,那么早在1500年前,我国古人又是怎么发现此问题的解题规律的呢?下面,小编就用列表法尝试着推测一下古人的解题思路。

1、《孙子歌诀》

仔细研读“物不知数”的解题思路发现,这道题的解题关键是发现了3个3个数、5个5个数、7个7个数的余数要乘的那3个系数70、21、15,和周期数105。明朝数学家程大位将解法规律所确定的这四个数编成易于上口的《孙子歌诀》:
三人同行七十稀,五树梅花廿一支,七子团圆正半月,除百零五使得知。

那么这四个数是怎么发现的呢?

2、利用列表法分析

(1)最小公倍数105的分析

下边先看表1,列出了被除数从0到105时,除数为3、5、7所对应的余数。那么被除数为0和105时余数都为0,从106开始又是和1是一样的,所以规律的周期就是105。也就是互质数3、5、7的最小公倍数。

表1 余数总表

被除数除数为3除数为5除数为7
0000
1111
2222
3033
4144
5205
6016
7120
8231
9042
10103
11214
12025
13136
14240
15001
16112
17223
18034
19145
20206
21010
22121
23232
24043
25104
26215
27026
28130
29241
30002
31113
32224
33035
34146
35200
36011
37122
38233
39044
40105
41216
42020
43131
44242
45003
46114
47225
48036
49140
50201
51012
52123
53234
54045
55106
56210
57021
58132
59243
60004
61115
62226
63030
64141
65202
66013
67124
68235
69046
70100
71211
72022
73133
74244
75005
76116
77220
78031
79142
80203
81014
82125
83236
84040
85101
86212
87023
88134
89245
90006
91110
92221
93032
94143
95204
96015
97126
98230
99041
100102
101213
102024
103135
104246
105000

从表1可以看出,根据三个余数,就可以找到对应的被除数。那么我们不可能去每给出一组余数,我们就去查一次表,我们需要去寻找这个被除数和每组余数之间的规律,根据规律来快速计算出结果。寻找规律要由最简单的情形开始,当两个除数的对应余数是固定的且都为0时,看看第三个除数对应的余数依次增加1时的规律。

(2)除7的余数对应系数15的解析

假如当除3、除5的余数都是0时,当除7余数以步伐为1从0递增到6时,我们来看看被除数有什么变化规律?从表1中筛选出除3余0并且除5余0的情况如表2所示。

表2  除3余0且除5余0表

被除数除数为3除数为5除数为7
0000
15001
30002
45003
60004
75005
90006

从表2可以看出,搜出数据一共7行,正好对应余数从0到6,而且余数是依次加1的。除7余0对应的被除数为0,除7余1对应的被除数为15,除7余2对应的被除数为30,……,依次增加15!这就是除7余数对应的系数为15的原因,且15=3*5,为除数7之外的两个除数最小公倍数。

(3)除5的余数对应系数21的解析

同样,假如当除3、除7的余数都是0,当除5余数每增加1时,我们来看看被除数有什么变化规律?从表1中筛选出除3余0并且除7余0的情况如表3所示。

表3  除3余0并且除7余0表

被除数除数为3除数为5除数为7
0000
21010
42020
63030
84040

从表3可以看出,搜出数据一共5行,正好对应余数从0到4,而且余数是依次加1的。除5余0对应的被除数为0,除5余1对应的被除数为21,除5余2对应的被除数为42,……,依次增加21!这就是除5余数对应的系数为21的原因,且21=3*7,为除数5之外的两个除数最小公倍数。

(4)除3的余数对应系数70的解析

同样,假如当除5、除7的余数都是0,当除3余数每增加1时,我们来看看被除数有什么变化规律?从表1中筛选出除5余0并且除7余0的情况如表4所示。

表4  除5余0并且除7余0表

被除数除数为3除数为5除数为7
0000
35200
70100

从表4可以看出,搜出数据一共3行,正好对应余数从0到2,每行的被除数增值为35。但是余数却不是依次加1,而是0、2、1,从0到1跨过了2,也就是从0到1被除数增值为70!这条规律与前两条是不同的。这就是除3余数对应的系数为70的原因。

为了更清楚地看出规律,我们从两个周期210个被除数中搜索除5余0、除7余0的情况,如表5所示,除数为3所在列第一行余数为0,被除数为0;第三行余数为1,被除数为70;第五行余数为2,被除数为140。除3列余数的规律为隔行增加1,余数0、1对应的被除数在第一个周期105以内,而余数2对应的被除数是在下一个周期105-210之内。这也就是为什么会出现计算结果大于105的原因

表5  除5余0并且除7余0两个周期表

被除数除数为3除数为5除数为7
0000
35200
70100
105000
140200
175100

(5)《孙子算经》“不知其数”结果分析

按着“不知其数”的题目,我们以除3余2,除5余3,除7余2为例进行分析。

按照公式,计算结果如下:

被除数=2×70+3×21+2×15=233,233-105=128,128-105=23。

减了两次105,才得出结果,那么还用列表来分析一下,为什么会减两次。

(a)先看当除3余2且除5余3时,除7余数的两个周期的规律如表6所示,余数为2在两个周期都出现了,那么哪一个才是正确的那个呢?

下边给出一条定律(暂称爬爬虫定律1):余数为0所在的第一行为起始行,其后依次出现的1、2、3……对应的行所在的周期就是被除数所在最小周期。

根据以上定律,除数为7的余数一共有两个周期,余数为0的起始位是在第7行,其后的余数1在第8行,余数1之后的余数2在第9行,对应的被除数为128,在区间[105,210]内,属于第二个周期。所以按照除7列,计算结果所在最小的周期为第二个周期。

表6  除3余2且除5余3两个周期表

被除数除数为3除数为5除数为7
8231
23232
38233
53234
68235
83236
98230
113231
128232
143233
158234
173235
188236
203230

(b)再看当除3余2且除7余2时,除5余数的两个周期的规律如表7所示,余数为3在两个周期都出现了,那么哪一个才是正确的那个呢?

表7  除3余2且除7余2两个周期表

被除数除数为3除数为5除数为7
2222
23232
44242
65202
86212
107222
128232
149242
170202
191212

根据爬爬虫定律1,先找到余数0所在行,为第4行,然后依次找到余数1、2、3所在的行分别为5、6、7,第7行对应的被除数为128,在[105,210]之间,属于第二个周期。所以按照除5列,计算结果也在第二个周期。

(c)最后看当除5余3且除7余2时,除3余数的三个周期的规律如表8所示,余数为2在三个周期都出现了,那么哪一个才是正确的那个呢?

表8  除5余3且除7余2三个周期表

被除数除数为3除数为5除数为7
23232
58132
93032
128232
163132
198032
233232
268132
303032

根据爬爬虫定律1,先找到余数0所在的起始行为第3行,其后的余数1在第5行,余数1之后的余数2在第7行,该行对应的被除数为233,在区间[210,315]之内,属于第三个周期。所以按照除3列,计算结果在第三个周期。

那么问题又来了,按照除7列,计算结果在第二周期,按照除5列,计算结果在第二周期,按照除3列,计算结果在第三周期。那么到底是在第二周期还是第三周期呢?

爬爬虫定律2:被除数所在周期为所有除数列计算出的周期中的最大周期。

根据爬爬虫定律2,第三周期为最大周期,也就是最终计算结果落在第三周期,那么105以内的最小值就是在计算结果上减去两个周期,即233-2×105=33。

至此,用列表法圆满解释了《孙子算经》之“不知其数”问题的三个系数、最小公倍数和计算结果减去周期个数的规律。

三、为什么要研究算法

有人说:“现在都有计算机了,还搞那么复杂干啥,你花了好几天时间,写了一大堆,还不如花几分钟时间写个程序计算一下,又快又准。”于是呼呼呼,写出了以下代码:

1、效率最低的算法

#include <stdio.h>
#include <string.h>
 
int main()
{
    int r3,r5,r7,d,i;

    scanf("%d %d %d",&r3,&r5,&r7); 
	
    for(i=0;i<105;i++)
    {
	    d=i;
        if((i % 7)==r7) && (i % 5)==r5 && (i % 3)==r3)
        break;
    } 
 
    printf("所求的数为:%d\n",d); 
    return d;
}

这种遍历的方法确实能算出答案,但效率太低,最多需要105+15+3=123步计算,说是算出来的,不如说是比对出来的,严格来说,都不能叫算法。

2、高级一点的算法

#include <stdio.h>
#include <string.h>
 
int main()
{
    int r3,r5,r7,d,i,j;    

    scanf("%d %d %d",&r3,&r5,&r7); 
	j=0;
   
    for(i=0;i<15;i++)
    {
	    d=i*7+r7;
        if((d % 5)==r5)

            {
                 j++;
                 if((d % 3)==r3)
                    {
                         break;
                    }
            }
        
    } 
 
    printf("所求的数为:%d\n",d); 
    printf("计算步数为:%d\n",(i+1)*2+j); 
    return d;
}

这种算法只在符合除7余数的15个数字中进行比对,最多计算步数:15+15+3=33步,步数降低很多,但代码又变复杂了。

3、孙子算经算法

#include <stdio.h>
#include <string.h>
 
int main()
{
    int r3,r5,r7,d,i;   

    scanf("%d %d %d",&r3,&r5,&r7); 
    i=0;
    d=r3*70+r5*21+r7*15;
    while(d>105)
    {
        i++;
        d=d-105;
    } 
 
    printf("所求的数为:%d\n",d); 
    printf("计算步数为:%d\n",i+1); 
    return d;
}

这种算法代码量最少,最多计算步数:1+2=3步,也是最少。

三种算法,孰优孰劣,一目了然。尽可能用最少的开支完成更多的计算量,让计算机系统更加高效工作,这就是研究算法的目的所在吧。

文章中如有谬误之处,拜请各路大神指正。

题外话:虽然现在IT行业,无论硬件的控制器芯片,还是软件类的开发平台,还是各种协议标准,还是各类主流算法都是欧美主导,我们还处于学习和追赶阶段。但很多经典算法其实也是源于古代数学思维,而我国古代有很多出色的数学家如刘徽、赵爽、杨辉、祖冲之、贾宪、秦九韶、朱世杰、徐光启等,和许多著名数学论著,如《周髀算经》、《九章算术》、《海岛算经》、《五曹算经》、《孙子算经》、《夏侯阳算经》、《张丘建算经》、《五经算术》、《缉古算经》、《缀术》等等。而六爻八卦、天干地支、阴阳五行也都包含着十分丰富的逻辑思维,能从现代算法中看到古代逻辑思维的影子。无论世界如何变,我中华传统文化都是一座取之不尽用之不竭的宝库,而我辈也当继承发扬之。我们在科学技术上的落后,也只是从近现代开始的。所以我们不能盲目排外,但也没必要一味媚外,丧失了文化自信、科学自信和技术自信。使用外来的先进技术,目的是为我所用,促进自己技术进步,而不能因为用惯了别人的,就丧失了自我发展的动力和能力。我们的老祖宗能做到领先世界几百年上千年,我想我们也应该有这个决心和动力。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值