俄罗斯套娃(俄语:матрёшка),是俄罗斯民族特色的木制玩具,一般由多个一样图案的空心木娃娃一个套一个组成,最多可达十多个,通常为圆柱形,底部平坦可以直立。如果不太了解俄罗斯套娃是什么,请打开下面的俄罗斯套娃3D视频观看。
格物致知,你从俄罗斯套娃视频中感悟到了什么呢?也许有,但无法用语言表达出来。不要紧,俄罗斯套娃的思想投影到数学里就是“化归思想”了。
化归思想:一个大问题想不出解决办法,但知道同类的规模较小的问题的解决办法,于是尝试把问题减少一个单位,解决变化情况,如此类推,直到能解决的程度。
化归思想也可以直接解决数学问题。例如,求前n个奇数和:
1+3+5+....+[2(n-1)+1]=?。
首先,我们知道,前1个奇数的和是1,这个问题可以用化归思想解决。先把问题转化为:
{1+3+5+....+[2(n-2)+1]}+[2(n-1)+1]=?。
显然,大括号里的问题是原问题的套娃,因此只需要解决[2(n-1)+1]项的问题。把它转化为:
2(n-1)+1 = (n-1) + 1 +(n-1)。
根据数形结合,上面整式可以用下面的图形表示:
看了上图后,你应该得到了答案:
1+3+5+....+[2(n-1)+1]=n²。
数学化归思想应用到编程,就是“递归”。
下面先编写一个Python程序验证上面的结果。
。
运行结果:
练习题1:用化归思想推导下面式子的公式,然后编写Python递归程序验证。
1+7+...+[3(n-1)²+3(n-1)+1]=?。
练习题2:用化归思想推导(凸)n(n≥3)边形内角和公式,然后编写Python递归程序验证。
我们日常生活中的“套娃”:“从前有座山,山里有座庙,庙里有个老和尚在给小和尚讲故事,讲的什么故事?从前有座山,山里有座庙,庙里有个老和尚在给小和尚讲故事,讲的什么故事?从前有座山……”。这种套娃和俄罗斯套娃有什么区别?
第一点,“套娃”没有结束条件,“俄罗斯套娃”受加工条件限制,最多十多个;
第二点,“套娃”的每次copy没有缩小规模,“俄罗斯套娃”要套在里面,所以要越来越小。
显然,这个故事不符合“俄罗斯套娃”思想,也就是数学的化归思想,当然不是递归了。
在《Python算法图解》中提出把这故事改编为递归的故事,原文如下:
这个例子近似于递归现象,但是严格来说并不是递归,因为该例子会一直重复下去,没有终止条件,那就成了死循环。加个条件,就成了递归。例如:从前有座山,山里有座庙,庙里有个老和尚和小和尚,如果小和尚没睡着,老和尚就讲故事;从前有座山,山里有座庙…如果小和尚睡着了,老和尚就停止讲故事(这时递归调用就停止了)。
虽然加了结束条件,但每次copy没有缩小规模,从严格意义上来说,还不是递归。
从编程方面来说,计算机是无法理解所谓“规模”的,递归算法只是程序中的函数(有返回值)或过程(无返回值函数)调用自身的编程技巧。“调用自身”是“递”的过程,如果没有有效的终止调用自身的条件——“归”,最终会产生程序堆栈溢出错误。设置有效的终止条件是程序员的责任,计算机帮不上忙。设置有效的终止条件方法如下:
第一步:选定条件变量;
第二步:条件变量在函数调用自身时如何变化;
第三步:构建条件表达式;
第四步:判断条件是否能达到和不总是达到。
例题1:输入1个2≤n≤64的正整数n,用递归法计算1/2+1/4+1/8+...直到最后一项≤1/n的和。
分析:第一步,选定数列的项序数(1,2,3,......)为条件变量;第二步,函数调用自身时条件变量增加1;第三步,根据最后一项≤1/n的条件,得条件表达式pow(2, 条件变量)>=n;第四步,随着项序数增加,pow(2, 条件变量)增大,最终一定会超过或达到n。
程序编码:
运行结果:
练习题3:如果把例题1的代码改为下图的样子,请完善代码。
递归代码虽然简单,但它是内存吃货,甚至会把电脑卡死。因为每次递归调用都要把现场压入运行堆栈,如果现场状态太多或递归次数太大,还没到递归终止,运行堆栈就溢出了。所以,除非其他方法(例如循环)无法实现才采用递归算法,而且尽量使递归部分简单,设置限制范围使递归的次数尽量少。
最后,把上面故事改成下面这样更符合递归:
从前有座山,山里有片树林,风吹着树林沙沙作响,树林里有座庙,庙里有个老和尚在给小和尚讲故事,声音越来越小,越来越慢,最后湮没在风声中就停止了,讲的是什么故事呢?从前有座山,山里有片树林,风吹着树林沙沙作响,树林里有座庙,庙里有个老和尚在给小和尚讲故事,声音越来越小,越来越慢,最后湮没在风声中就停止了,讲的是什么故事呢?从前有座山,......
参考资料:
1、强基初中数学&学Python——第十六课 “套娃多项式”探究
2、强基初中数学&学Python——第136课 多边形、化归思想与递归算法
3、《Python算法图解》何韬编著 清华大学出版社