我在伪代码中表达算法。我只是想知道我的设计是否与下面显示的原始设计一样好。该算法应该计算n个奇数正整数之和。
这就是算法的外观:
procedure sumofodds(n:positive integer)
if n = 1
return 1
else
return sumofodds(n-1) + (2n-1)
这就是我设计算法的方法:
procedure odd(n: positive integer)
if n = 1
return 1
if n % 2 > 0
return n + odd(n-1) // this means n is odd
if n % 2 = 0
return 0 + odd(n-1) // this means its even
答案 0 :(得分:3)
您的算法与原始算法不同。
原始计算前n个奇数的总和。
您的算法计算1..n。
范围内所有奇数的总和因此,对于n = 3的输入,第一个算法将计算1 + 3 + 5,而您的算法将计算1 + 3。
(如果你想要更快的方法,那么公式n * n计算前n个奇数的总和)
答案 1 :(得分:1)
我建议你用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
。整数是奇数或偶数,所以如果它不是奇数,它必须是偶数。
当sum_of_odds_up_to(n-2)
为奇数时,通过递归调用n
可以获得另一个性能提升。目前,你在偶数上浪费了一半的函数调用。
通过这两项改进,代码变为:
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
答案 2 :(得分:0)
可能帮助的一个小改进是使用尾递归来定义它。当最后执行的事情是递归调用时,会发生尾递归。要使此尾递归,请使用辅助方法并将运行总和作为参数传递。我很确定下面的伪代码是尾递归的,因为无论(如果是奇数)检查的结果如何,最后一步是递归调用(数学在递归调用之前发生)。
procedure SumOdds(n)
return SumOddsHelper(n, 0)
procedure SumOddsHelper(n, sum)
if n = 1 return 1
if n is odd return SumOddsHelper(n-1, sum + n)
else return SumOddsHelper(n-1, sum)