【问题标题】:Recursive algorithm for the sum of odd number positive integers奇数正整数和的递归算法
【发布时间】:2014-12-05 22:52:05
【问题描述】:

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

【问题讨论】:

  • 谢谢。我写了同样的算法。请问您的实现有什么问题吗?

标签: algorithm recursion pseudocode


【解决方案1】:

你的算法和原来的不一样。

原始计算前n个奇数的总和。

您的算法计算 1..n 范围内所有奇数的总和。

所以对于 n=3 的输入,第一个算法将计算 1+3+5,而您的算法将计算 1+3。

(如果你想要更快的方法,那么公式 n*n 计算前 n 个奇数的总和)

【讨论】:

    【解决方案2】:

    可能帮助的一个小改进是用尾递归定义它。当执行的最后一件事是递归调用时,就会发生尾递归。要使此尾递归,请使用辅助方法并将运行总和作为参数传递。我很确定下面的伪代码是尾递归的,因为无论(如果奇数)检查的结果如何,最后一步都是递归调用(数学发生在递归调用之前)。

    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)
    

    【讨论】:

    • 感谢您展示这一点,但我的问题是我的算法是否正常工作。因为我的班级还没有学习尾递归,但我们很快就会
    • 乍一看还不错。由于您正在处理无效输入,它应该可以工作。
    • 好的,感谢您的输入,我也会更多地介绍尾递归。
    【解决方案3】:

    让我建议您在 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-06
      • 1970-01-01
      • 2017-07-23
      • 2018-12-10
      • 1970-01-01
      • 2020-01-15
      相关资源
      最近更新 更多