【问题标题】:Python program to calculate harmonic series计算谐波级数的Python程序
【发布时间】:2020-08-18 17:07:26
【问题描述】:

有谁知道如何用 Python 编写一个计算调和级数相加的程序。即 1 + 1/2 +1/3 +1/4...

【问题讨论】:

  • 调和级数的第一项是1

标签: python math


【解决方案1】:

@Kiv's answer 是正确的,但如果您不需要无限精度,那么对于大 n 来说它会很慢。在这种情况下最好使用asymptotic formula

#!/usr/bin/env python
from math import log

def H(n):
    """Returns an approximate value of n-th harmonic number.

       http://en.wikipedia.org/wiki/Harmonic_number
    """
    # Euler-Mascheroni constant
    gamma = 0.57721566490153286060651209008240243104215933593992
    return gamma + log(n) + 0.5/n - 1./(12*n**2) + 1./(120*n**4)

@Kiv's answer 用于 Python 2.6:

from fractions import Fraction

harmonic_number = lambda n: sum(Fraction(1, d) for d in xrange(1, n+1))

例子:

>>> N = 100
>>> h_exact = harmonic_number(N)
>>> h = H(N)
>>> rel_err = (abs(h - h_exact) / h_exact)
>>> print n, "%r" % h, "%.2g" % rel_err
100 5.1873775176396242 6.8e-16

N = 100 处的相对误差小于1e-15

【讨论】:

    【解决方案2】:

    @recursive's solution 对于浮点近似是正确的。如果您愿意,您可以使用分数模块在 Python 3.0 中获得确切答案:

    >>> from fractions import Fraction
    >>> def calc_harmonic(n):
    ...   return sum(Fraction(1, d) for d in range(1, n + 1))
    ...
    >>> calc_harmonic(20) # sum of the first 20 terms
    Fraction(55835135, 15519504)
    

    请注意,位数增长很快,因此对于较大的 n,这将需要大量内存。如果您想要真正花哨的话,您还可以使用生成器来查看一系列部分和。

    【讨论】:

    • 你有一个错误。 range(1, n) 生成 (n-1) 项目而不是 n 项目所需。前 20 个术语的总和是 55835135/ 15519504。见stackoverflow.com/questions/404346/…
    • 使用 xrange 防止它吃掉大量内存
    • 这是 Python 3.0 - xrange 已重命名为 range,旧的内存密集型 range 已不复存在。
    • @Kiv: Python 3.0 中的range 不仅重命名为xrange,例如,range 接受大整数但xrange 不接受。
    • 你是对的。这是由于 xrange 代码的更改,还是统一 int/long 类型的结果?两个版本都需要一个 int 参数,但是 int 的定义发生了变化。
    【解决方案3】:

    只是对使用浮点的其他答案的脚注;从最大的除数开始并向下迭代(朝着具有最大值的倒数)将尽可能推迟累积的舍入误差。

    【讨论】:

    • 我同意当你得到正数时,你可以产生从最小到最大的数字(就像这里)。但是,Python 3 包含“fsum”函数,它给出了一系列浮点数的正确浮点数(双精度)总和,而不管它们的顺序如何。我相信即使是最小到最大的“手动”求和也会产生错误,尤其是当数字有不同的符号时。我不知道其他语言的库是否包含fsum,但是你可以从Python的源代码中窃取C代码。
    【解决方案4】:

    可以使用 digamma 函数计算 H 函数的快速、准确、平滑、复值版本,如 here 所述。 Euler-Mascheroni (gamma) 常数和 digamma 函数分别在 numpy 和 scipy 库中提供。

    from numpy import euler_gamma from scipy.special import digamma def digamma_H(s): """ If s is complex the result becomes complex. """ return digamma(s + 1) + euler_gamma from fractions import Fraction def Kiv_H(n): return sum(Fraction(1, d) for d in xrange(1, n + 1)) def J_F_Sebastian_H(n): return euler_gamma + log(n) + 0.5/n - 1./(12*n**2) + 1./(120*n**4)

    下面是三种方法的速度和精度对比(以Kiv_H为参考):

    Kiv_H(x) J_F_Sebastian_H(x) digamma_H(x) x seconds bits seconds bits seconds bits 1 5.06e-05 exact 2.47e-06 8.8 1.16e-05 exact 10 4.45e-04 exact 3.25e-06 29.5 1.17e-05 52.6 100 7.64e-03 exact 3.65e-06 50.4 1.17e-05 exact 1000 7.62e-01 exact 5.92e-06 52.9 1.19e-05 exact

    【讨论】:

      【解决方案5】:

      调和级数发散,即其和为无穷大..

      编辑:除非你想要部分总和,但你并不太清楚。

      【讨论】:

      • 我假设他正在寻找一个有限的子系列,因为循环整个系列也需要无限长的时间。
      • 好吧,但这是一个没有实际意义的问题 - 这个系列无论如何都会发散,因此将无限数量的术语相加是徒劳的。
      【解决方案6】:

      这应该可以解决问题。

      def calc_harmonic(n):
          return sum(1.0/d for d in range(2,n+1))
      

      【讨论】:

      • 如果你使用 Python 3,你可以使用 1/d 而不是 1.0/d
      • 如果你使用 xrange,这不会为大 n 使用过多的内存
      • 还要注意使用浮点数时精度的损失——@Kiv 的回答会产生所有 n 的准确值。
      • @cdleary:你打算如何处理确切的答案?分子和分母呈指数增长,因此使用浮点数是个好主意。 [而且打印近似值要快得多;只需打印“ln n + 0.5772156649”——见en.wikipedia.org/wiki/Euler-Mascheroni_constant]
      • 以这种方式对浮点数求和并不是一个好主意。至少先把列表倒过来。或者使用np.sum,它将进行成对求和。
      【解决方案7】:

      这个怎么样:

      partialsum = 0
      for i in xrange(1,1000000):
          partialsum += 1.0 / i
      print partialsum
      

      其中 1000000 是上限。

      【讨论】:

      • 当您分配给 sum 时,您将覆盖一个内置函数。我尽量避免这样做,因为如果您稍后尝试调用该函数,它可能会导致奇怪的错误。
      • 另外,还有一件小事:你的第一个学期是 1/1。在问题中是 1/2。
      • 如果我没记错的话,谐波级数的第一项是1/1。
      • 我也是这么学的,但问题不是这样的。哦,好吧。
      • 嗯,谐波级数从 1 开始,所以我选择从 1 开始。修复起来并不难:)
      【解决方案8】:

      家庭作业?

      这是一个发散级数,因此不可能对所有项进行总结。

      我不懂 Python,但我知道如何用 Java 编写它。

      public class Harmonic
      {
          private static final int DEFAULT_NUM_TERMS = 10;
      
          public static void main(String[] args)
          {
              int numTerms = ((args.length > 0) ? Integer.parseInt(args[0]) : DEFAULT_NUM_TERMS);
      
              System.out.println("sum of " + numTerms + " terms=" + sum(numTerms));
           }
      
           public static double sum(int numTerms)
           {
               double sum = 0.0;
      
               if (numTerms > 0)
               {
                   for (int k = 1; k <= numTerms; ++k)
                   {
                       sum += 1.0/k;
                   }
               }
      
               return sum;
           }
       }
      

      【讨论】:

      • 它作为一个“计算谐波级数的python程序”确实有点失败
      • 但当然,如果有人真诚地想要进行计算,他们可以从这里拼凑起来。 [自我说明:学习python]
      • 我认为对事物有另一种看法总是好的。
      • 您是认真地说 Java 比 Python 更接近伪代码还是更易于理解或更高级别?
      • 您是否真的对一个八岁的答案感到兴奋?我说出了我的意思:我的额叶皮层顶端有 Java,所以我写了 Java。如果 OP 想将它翻译成其他东西,它应该足够清楚。在这里找到另一种方法来提高您的低声誉。
      【解决方案9】:

      使用简单的 for 循环

      def harmonicNumber(n):
      x=0
      for i in range (0,n):
          x=x+ 1/(i+1)
      return x
      

      【讨论】:

        【解决方案10】:

        我添加了另一个解决方案,这次使用递归来找到第 n 个谐波数。

        一般实施细节

        函数原型: harmonic_recursive(n)

        函数参数: n - 第n次谐波数

        基本情况:如果n 等于1,则返回1。

        重复步骤: 如果不是基本情况,请调用 harmonic_recursive 获取 n-1 术语并将结果与​​ 1/n 添加。这样,我们每次将谐波级数的第 i 项与之前所有项的总和相加。

        伪代码

        (这个解决方案也可以很容易地用其他语言实现。)

        harmonic_recursive(n):
            if n == 1:
                return 1
            else:
                return 1/n + harmonic_recursive(n-1)
        

        Python 代码

        def harmonic_recursive(n):
            if n == 1:
                return 1
            else:
                return 1.0/n + harmonic_recursive(n-1)
        

        【讨论】:

        • 与已经展示的迭代或近似方法相比,该递归代码是否有一些优势?递归有时比迭代更清晰或更自然,但在这种情况下并非如此,因此应避免其缓慢的速度。您的代码似乎对教授递归很有用,但对回答问题没有用——但我可能错了。
        • @RoryDaulton 真实的事实。但我的代码并不比迭代代码慢。我使用线性递归,这意味着函数体最多进行一次新的递归调用。时间空间效率最多为O(n)。 -我不知道你是否愿意写证明。-
        • 我在你的函数上使用了timeit,在 Python 3.6.1 中使用了一个等效的迭代函数 n=1000,你的代码得到了 420 µs,迭代函数得到了 140 µs。一般的函数调用和特别是 Python 中的递归调用是昂贵的。我确实在 Python 中使用了递归,但在迭代同样清晰时却没有。当然,这里的时间差绝对值很小,所以除非 n 很大,否则这是无稽之谈。
        • @RoryDaulton 好点。我自己测量的。发生这种情况是因为每次调用函数(递归或其他方式)时,都会创建一个称为activation record 或框架的结构来存储函数的状态。这是昂贵的,尤其是对于递归函数。感谢您指出。
        【解决方案11】:

        通过使用numpy 模块,您也可以选择使用:

        import numpy as np
        def HN(n):
            return sum(1/arange(1,n+1))
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-11-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多