是否可在不使用递归与while循环的前提下重写递归函数findNum?
可以!用数学公式直接重写,无需递归或循环
首先先把原递归函数的代码贴出来,方便我们拆解逻辑:
function findNum(a, b) { if (!b) { b = 0; } if (a < 2) { throw new Error('wrong'); } if (a === 2) { return 1 / a + b; } return findNum(a - 1, b + 1 / (a * (a -1))); }
拆解原函数的递归本质
原函数的核心逻辑是通过递归累加一个数列:每次递归时,会把1/(a*(a-1))加到参数b中,直到a递减到2,最后返回1/2 + b。如果把这个累加过程展开(假设初始b=0),实际计算的是:1/(n*(n-1)) + 1/((n-1)*(n-2)) + ... + 1/(3*2) + 1/2
用裂项相消化简级数
这里可以用到分式裂项的数学技巧:1/(k*(k-1)) = 1/(k-1) - 1/k。把每一项拆开来后,累加的中间项会相互抵消:
1/(3*2) = 1/2 - 1/31/(4*3) = 1/3 - 1/4- ...
1/(n*(n-1)) = 1/(n-1) - 1/n
把这些项加起来,中间的-1/3和+1/3、-1/4和+1/4都会抵消,最后剩下1/2 - 1/n。再加上原函数最后返回的1/2,总和就是:(1/2 - 1/n) + 1/2 = 1 - 1/n
如果初始传入了非0的b,那最终结果就是b + 1 - 1/a(因为b会被一直累加进去)。
重写后的无递归/无循环函数
基于上面的推导,我们可以直接写出简化版的函数,完全不需要递归或循环:
function findNum(a, b) { // 保留原函数的b参数默认值处理 const initialB = b ?? 0; // 保留原函数的错误校验逻辑 if (a < 2) { throw new Error('wrong'); } // 直接用化简后的公式计算 return initialB + 1 - 1 / a; }
验证正确性
举几个例子验证和原函数结果一致:
- 调用
findNum(2):返回1 - 1/2 = 0.5,和原函数返回1/2 + 0完全一致; - 调用
findNum(3):返回1 - 1/3 ≈ 0.6667,原函数递归计算为1/(3*2) + 1/2 = 1/6 + 1/2 = 2/3,结果一致; - 调用
findNum(4, 2):返回2 + 1 - 1/4 = 2.75,原函数递归计算为2 + 1/(4*3) + 1/(3*2) + 1/2 = 2 + 1/12 + 1/6 + 1/2 = 2.75,结果完全匹配。
内容的提问来源于stack exchange,提问作者Adope Adopish




