让我建议您在 Python 中实现您的想法。您可能会惊讶地发现工作代码与伪代码非常相似。
这是原始算法:
def sum_of_n_odds(n):
if n == 1:
return 1
else:
return sum_of_n_odds(n-1) + (2*n-1)
这是你写的:
def sum_of_odds_up_to_n(n):
if n == 1:
return 1
if n % 2 > 0: # this means n is odd
return n + sum_of_odds_up_to_n(n-1)
if n % 2 == 0: # this means it's even
return 0 + sum_of_odds_up_to_n(n-1)
这两种算法计算不同的东西。调用sum_of_n_odds(10) 产生与调用sum_of_odds_up_to_n(19) 或sum_of_odds_up_to_n(20) 相同的结果。一般情况下,sum_of_odds_up_to_n(n) 等价于sum_of_n_odds((n+1)//2),其中// 表示整数除法。
如果您有兴趣使您的实现更加高效,我建议您省略最后的if 条件,其中n % 2 == 0。一个整数要么是奇数要么是偶数,所以如果它不是奇数,它一定是偶数。
当n 为奇数时,您可以通过递归调用sum_of_odds_up_to(n-2) 获得另一个性能提升。目前,您将一半的函数调用浪费在偶数上。
通过这两个改进,代码变为:
def sum_of_odds_up_to_n(n):
if n <= 0:
return 0
if n % 2 == 0:
return sum_of_odds_up_to_n(n-1)
return n + sum_of_odds_up_to_n(n-2)
这是尾递归版本:
def sum_of_odds_up_to_n(n, partial=0):
if n <= 0:
return partial
if n % 2 == 0:
return sum_of_odds_up_to_n(n-1, partial)
return sum_of_odds_up_to_n(n-2, partial+n)
你不应该期望从上面得到性能提升,因为 Python 没有针对尾递归进行优化。但是,您可以将尾递归重写为迭代,这将运行得更快,因为它不会花费时间为每个递归调用分配堆栈帧:
def sum_of_odds_up_to_n(n):
partial = 0
if n % 2 == 0:
n -= 1
while n > 0:
partial += n
n -= 2
return partial
最快的实现依赖于数学洞察力。考虑总和:
1 + 3 + 5 + ... + (n-4) + (n-2) + n
请注意,您可以将第一个元素与最后一个元素配对,将第二个元素与倒数第二个元素配对,将第三个元素与倒数第三个元素配对,以此类推:
(1 + n) + (3 + n-2) + (5 + n-4) + ...
很容易看出这等于:
(n + 1) + (n + 1) + (n + 1) + ...
(n + 1) 有多少个术语?由于我们一次将原始序列中的两个术语配对,因此(n + 1) 序列中的术语数量是其一半。
您可以自己检查原始序列是否包含(n + 1) / 2 术语。 (提示:看看每一项加 1 会得到什么。)
新序列的词条数量是原来的一半,即(n + 1) / 4。而序列中的每一项都是(n + 1),所以整个序列的总和是:
(n + 1) * (n + 1) / 4
生成的 Python 程序是这样的:
def sum_of_odds_up_to_n(n):
if n <= 0:
return 0
if n % 2 == 0:
n -= 1
return (n+1)*(n+1)//4