一道北大强基题背后的故事(一)——从走弯路到看答案

2023-07-12 15:00:34 浏览数 (1)

在前面的系列文章《我的数学学习回忆录——一个数学爱好者的反思(二)》中,我从宏观层面回忆了我的数学学习历程和反思。其实,我和数学之间还有很多很多意识流一样的交流和故事,它会时不时在我的生活中可爱地蹦跶出来。有时源于突然记起的公式,有时源于工作生活中联想回去的特定场景。它代表着我那时候的记忆定格以及以我今天的思维碰撞后的结果,有时能擦出令人惊喜的思维火花。

我很乐意记录和分享这些学习思考的乐趣,它也像砖瓦一样构筑着让我觉得安全的思考大厦。

这个系列的灵感源于看到一道北大强基考试题,先看题:

计算:[((1 sqrt(5)) / 2) ^ 12]

这是一道整数部分估算题。

那就来看看我的解题和思考之旅吧!

算法逻辑的硬算

如今的我,早就忘了当年的各种奇淫技巧,倒是对用计算机思维程序性地解决问题熟练在心。于是我二话不说,直接开始按程序逻辑硬算。

首先,需要解析这个表达式成一个假想的计算树结构,树顶(也就处理它用的栈的是栈底)的最后一步计算是向下取整,即所有小于等于它的整数中的最大值。如果是精确表达的有限小数,算出来后抹去小数点后的即为所求;如果是有理数,本质也一样,可以化为分数以后化为连分数后剔除不足1的部分即为所求。负数的话问题也不大,注意符号带来的变化就是了,总之都是可以按逻辑执行解决的。

但是,如果对象是无理数(容易证明非完全平方数的sqrt都是无理数,以之为唯一参数x,仅用一次的有理数内四则运算后的p / q * x a仍然为无理数;更多根号有理数的和,包括pi,n次方根的情况需要另外讨论),那理论上需要求出一个值域范围,不能包含任何不在边界上的整数,而不是那种可以左右摇摆的四舍五入近似结果,才能保证下界准确求得。(根据整数部分的定义证明)

显然这里取整运算之前是无理数,因为1 sqrt(5)) / 2就是上述无理数,而12次方根据二项式定理,剔除掉那些因为平方变回有理数的部分,剩余的依然是p / q * x a的计算形式的无理数。

故我们需要求得取整前的无理数的估算值,更准确来说是一个足够计算整数部分值的开区间范围。好在,所有的计算无非在四则运算范围内,那自然可以用计算机提供的相关接口解决,由此估算出一个上下限内不包含整数的范围即为所求。

另外,在带入近似解去求范围之前,本身可以把sqrt(5)的无理数部分当成参数进行符号运算,并且可以用sqrt(5) ^ 2 = 5的式子消解和化简,这种符号计算依旧可抽象为CFG语法描述的AST树的解析,并无不可解之处。这属于计算机代数系统,最有名的莫过于wolfram的系统,在python,matlab等上也都有相应实现。

另外,按数学里学过的二项式定理,它本质上是形如(ax by) ^ n的二元一次完全幂次的多项式计算的结论公式,因为其系数的计算刚好符合组合数公式,有结果的优雅性。而从计算角度而言,用指数计算本身有复杂度为o(logn)级别的算法,此时复杂度为o(n ^ 2 * logn),n ^ 2为平方一个多项式的复杂度;而用上二项式定理结论的话则是o(n)(后一项的计算都可以由前一项在单位时间内计算而得);如果是求其中一个特殊的指定x次幂为m的一项,还可以进一步优化到o(m),但直接按指数算的就没有任何优势了。这里可以初步看到,对数学规律的符号化挖掘,发现规律,用了更好的数学计算模型以后,对计算速度的帮助。

然而,这里需要令sqrt(5) = y,才符合以上的一切推理,而忽略了sqrt(5)这个数的定义带来的一个非常重要的sqrt(5) ^ 2 = 5的化简性质。这使得指数算法的复杂度又瞬间降低到了o(logn),因为每次产生的二次项都会被消掉,使得实际要平方计算的项数永远都只有2项,等价的次幂仍然是1次。

当我处在一个有计算机资源的环境下,哪怕只需要解决一个问题,我也一定力图写出通用代码批量解决这一类问题,而不是一个。当然,如果解决一类的方法在解决一个时仍然高效,那为了这个一个也要把一类解决了。当然,思维的抽象层次可以很高,但是代码的层次需要适当。

这里,根据指数计算算法,我们尝试把取整里面的式子化为:(X * X ^ 2) ^ 2 ^ 2,X = (1 sqrt(5)) / 2。

这显然就剩下了4次运算了,比12就这个特例都省去了2 / 3的计算次数。不过,这在并非n趋于无穷的趋势下的特例上就绝对有效,以及还要人类对每种运算本身的单次运算时间的差距,这都很影响计算时间估计的系数,在无穷时是弟弟,具体数值时可就猴子称霸王了。同时,我们还需要了解自己,知道自己在哪种类型的计算上的效率最高,用准确率和速度都相对有最大优势的方法去计算,找到自己当下水平估计下的预期以及此时的执行最优解,这也是分析的一部分。

于是这里很容易可以求得:

(X * X ^ 2) ^ 2 ^ 2 = ((1 sqrt(5)) / 2 * (3 sqrt(5)) / 2) ^ 2 ^ 2 = (2 sqrt(5)) ^ 2 ^ 2 = (9 4sqrt(5)) ^ 2 = 161 72sqrt(5)

当然,这里是严格按照递归算法的思路手动拆解的结果,而实际上我们正向计算的时候,一般会算到X ^ 2, X ^ 4, X ^ 8,然后发现12 = 8 4,就地取材把值算出来了。不过对计算机而言,上面的递归更好描述,而这个方法,倒是有点像进制数分解算法。复杂度也相同,只是这么迭代更符合人脑的处理逻辑,不用有堆那么多栈的压力,还能完成一些特殊条件下的巧算。

再把sqrt(5) = 2.236带入计算,即可求出估算值。

算法逻辑的碰壁

我以为这题就要被我暴力地搞出来了,但是要严谨地证明式地把值放心地求出来,可不是一件容易的事。

注意前面说了,这里理论上要严格求整数部分,需要取得一个sqrt(5)的一个上下界,使得最后的结果范围也不包含任何整数,然后看到161不影响整数包含情况先剔除。

72放大到100的话,需要0.01左右级别的精度,故计算下2.23得160.56,如果2.237时小于161就得证了。但一算发现,居然超过了,只能继续提高精度。在2.236上则是160.992,然而我们一般记忆到sqrt(5)的小数点后3位时的6,代表着有可能是2.2355 ...或者2.2364-...,后者竟然又超过了161,这就让想确定最终答案是321还是322的孩子们十分纠结了。如果你算到这时候放弃了,那你就只值得期望为0.5的得分值,可别怪什么运气不好。

不行,一条道走到黑,得动用终极大招,开方公式!再往下算一位小数,发现是2.2360,不超过161,但2.2361仍然超过,再算到2.23606和2.23607这两个数把sqrt(5)夹在中间,才最终确认161的整数部分成立。

奇妙的数学解法

好吧,写了这么多就只是为了记录一个严谨而愚蠢的错误思路。虽然得到了答案,但这个计算量,精度,速度,细心和耐心的要求已经远远超出了一个正常自主招生题考察的本意,也使得凡人基本不可能从这条直接的通路上在有限时间内而脑算出答案。

也就是说,计算机通用的那套方法,在解题思路上,和有限条件下折中的方法上,行不通啊!

我们还需要在特定时间条件下,对给定的目标问题,拆解规划出真正可行的思路。

此时我开始回想,在儿时学习解数学题的过程中,本就从来都没有这么考察硬算的题目(纯考试经验技巧,无道理可言)。从今天的观点看虽然是几乎唯一的逻辑解,但那是没有考虑人脑以及有限时间的条件的。而数学题给我眼前一亮的表面原因一直是,竟然可以这么做,好漂亮的解法!

这时,我们来看一下神奇的解答:

a. 构造斐波那契数列:b_(n 2) = b_(n 1) b_n,令b1 = 1, b2 = 3;

b. 递推计算,知b12 = 322;

c. 写出bn的通项公式:bn = ((1 sqrt(5)) / 2) ^ n ((1 - sqrt(5)) / 2) ^ n;

d. 取n = 12, ((1 sqrt(5)) / 2) ^ 12 ((1 - sqrt(5)) / 2) ^ 12 = 322;

e. 因为((1 - sqrt(5)) / 2) ^ 12必然在(0, 1)内,因此[((1 sqrt(5)) / 2) ^ n] = b12 - 1 = 321。

没错,就这么完了!

但是对此题的真正理解才刚刚开始,出题人到底是怎么想出这题的,这么考察合适吗?还是只是魔术秘密般毫无规律的巧合?

以及,我们要如何学习解题?解数学题的本质到底是什么?

在日思夜想中,我不断探究着它的本质,欲知后事如何,下期见!

0 人点赞