奇数正整数之和的递归算法

时间:2014-12-05 22:52:05

标签: algorithm recursion pseudocode

我在伪代码中表达算法。我只是想知道我的设计是否与下面显示的原始设计一样好。该算法应该计算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

3 个答案:

答案 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)