8

Does anyone know how to write a program in Python that will calculate the addition of the harmonic series. i.e. 1 + 1/2 +1/3 +1/4...

Zero Piraeus
  • 56,143
  • 27
  • 150
  • 160
marc lincoln
  • 1,551
  • 4
  • 21
  • 25

11 Answers11

22

@Kiv's answer is correct but it is slow for large n if you don't need an infinite precision. It is better to use an asymptotic formula in this case:

asymptotic expansion for harmonic number

#!/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 for Python 2.6:

from fractions import Fraction

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

Example:

>>> 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

At N = 100 relative error is less then 1e-15.

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
13

@recursive's solution is correct for a floating point approximation. If you prefer, you can get the exact answer in Python 3.0 using the fractions module:

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

Note that the number of digits grows quickly so this will require a lot of memory for large n. You could also use a generator to look at the series of partial sums if you wanted to get really fancy.

Community
  • 1
  • 1
Kiv
  • 31,940
  • 6
  • 44
  • 59
  • You have off-by-one error. `range(1, n)` produces `(n-1)` items not `n` items as required. Sum of the first 20 terms is `55835135/ 15519504`. See http://stackoverflow.com/questions/404346/python-program-to-calculate-harmonic-series#404843 – jfs Jan 01 '09 at 10:56
  • Use xrange to prevent it from eating tons of memory – zenazn Jan 01 '09 at 15:34
  • This is Python 3.0 - xrange has been renamed range and the old memory-hungry range is gone. – Kiv Jan 01 '09 at 16:08
  • @Kiv: `range` in Python 3.0 is not just renamed `xrange` e.g., `range` accepts large integers but `xrange` doesn't. – jfs Jan 01 '09 at 16:46
  • You're right. Is this due to a change in the xrange code, or a consequence of unifying the int/long types? Both versions require an int argument, but the definition of an int changed. – Kiv Jan 01 '09 at 17:21
  • Both. rangeobject.c uses C's `long` type in Python 2.x and "PyLong" in py3k. – jfs Jan 01 '09 at 18:15
6

A fast, accurate, smooth, complex-valued version of the H function can be calculated using the digamma function as explained here. The Euler-Mascheroni (gamma) constant and the digamma function are available in the numpy and scipy libraries, respectively.

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)


Here's a comparison of the three methods for speed and precision (with Kiv_H for reference):

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

FutureNerd
  • 769
  • 7
  • 9
6

Just a footnote on the other answers that used floating point; starting with the largest divisor and iterating downward (toward the reciprocals with largest value) will put off accumulated round-off error as much as possible.

joel.neely
  • 30,725
  • 9
  • 56
  • 64
  • I agree when you've got positive numbers you can produce smallest-to largest (like here). But, Python 3 includes the "fsum" function which gives the correct float (double-precision) sum of a sequence of floats, regardless of their order. I believe "manually" summing even smallest-to-largest can produce errors, especially when the numbers have different signs. I don't know whether the libraries of other languages include fsum yet, but you can steal the C code from Python's sources. – SteveWithamDuplicate Mar 22 '20 at 04:54
4

The harmonic series diverges, i.e. its sum is infinity..

edit: Unless you want partial sums, but you weren't really clear about that.

dancavallaro
  • 13,109
  • 8
  • 37
  • 33
  • I was assuming he was looking for a finite sub-series, since looping over the whole series would also take infinitely long. – recursive Jan 01 '09 at 01:03
  • Well, but that's a moot point - the series diverges anyway, so adding up an infinite number of terms is an exercise in futility. – dancavallaro Jan 01 '09 at 01:06
2

This ought to do the trick.

def calc_harmonic(n):
    return sum(1.0/d for d in range(2,n+1))
recursive
  • 83,943
  • 34
  • 151
  • 241
  • If you are using Python 3 you can do 1/d instead of 1.0/d – mdec Jan 01 '09 at 01:01
  • If you use xrange, this doesn't use an exorbitant amount of memory for large n – zenazn Jan 01 '09 at 01:15
  • Also note the loss of precision in using floating point numbers -- @Kiv's answer yields the exact value for all n. – cdleary Jan 01 '09 at 02:39
  • @cdleary: What are you going to do with the exact answer? The numerator and denominator grow exponentially, so using floating points is a good idea. [And it's much much faster to print an approximation; just print "ln n + 0.5772156649" -- see http://en.wikipedia.org/wiki/Euler-Mascheroni_constant ] – ShreevatsaR Jan 01 '09 at 14:16
  • 1
    It isn't a good idea to sum floating point numbers this way. At least reverse the list first. Or use `np.sum`, which will do pairwise summation. – Richard Sep 01 '17 at 05:22
0

How about this:

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

where 1000000 is the upper bound.

zenazn
  • 14,295
  • 2
  • 36
  • 26
  • When you assign to sum, you're overwriting a builtin function. I try to avoid doing that, because it can lead to weird bugs if you try to call that function later. – recursive Jan 01 '09 at 01:03
  • Also, another minor thing: your first term is 1/1. In the question it's 1/2. – recursive Jan 01 '09 at 01:06
  • If I remember correctly, the first term of the harmonic series is 1/1. – dancavallaro Jan 01 '09 at 01:08
  • That's how I learned it too, but the question says otherwise. Oh well. – recursive Jan 01 '09 at 01:09
  • Well, the harmonic series starts with 1, so that's where I chose to start. It's not hard to fix :) – zenazn Jan 01 '09 at 01:09
  • @zenazn: just as a Python-idiom note, if you want to use the name of a built-in function, it's customary to append an underscore to it; i.e. sum_ = 0 – cdleary Jan 01 '09 at 02:41
  • Adding floating-point numbers sequentially beginning with the largest number is the worst way of doing such a summation. – Richard Sep 01 '17 at 05:23
0

Homework?

It's a divergent series, so it's impossible to sum it for all terms.

I don't know Python, but I know how to write it in 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;
     }
 }
duffymo
  • 305,152
  • 44
  • 369
  • 561
  • 1
    It does sort of fail at being a "python program to calculate harmonic series" – J.T. Hurley Jan 01 '09 at 14:34
  • but certainly if somebody was sincere at wanting to do the calculation they could piece it together from this. [note to self: learn python] – duffymo Jan 01 '09 at 15:14
  • I think it's always nice to have an alternative perspective on things. – Ali Afshar Jan 01 '09 at 16:02
  • Are you seriously saying that Java is closer to pseudo-code than Python or that it is more understandable or higher level? – Rory Daulton May 20 '17 at 11:30
  • Are you seriously getting worked up about an eight year old answer? I said what I meant: I had Java on the tip of my frontal cortex, so I wrote Java. If the OP wanted to translate it to something else it should be clear enough. Find another way to boost your low reputation here. – duffymo May 20 '17 at 12:31
  • I am surprised at the vehemence of your response. I was not worked up but was referring to your comment about Java as pseudo-code. A serious question: how could my comment boot my reputation score? Or did you mean my reputation apart from my score? If you don't like comments on your old questions, I'll refrain from adding any more beyond this one. – Rory Daulton May 20 '17 at 14:34
0

Using the simple for loop

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

I add another solution, this time using recursion, to find the n-th Harmonic number.

General implementation details

Function Prototype: harmonic_recursive(n)

Function Parameters: n - the n-th Harmonic number

Base case: If n equals 1 return 1.

Recur step: If not the base case, call harmonic_recursive for the n-1 term and add that result with 1/n. This way we add each time the i-th term of the Harmonic series with the sum of all the previous terms until that point.

Pseudocode

(this solution can be implemented easily in other languages too.)

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

Python code

def harmonic_recursive(n):
    if n == 1:
        return 1
    else:
        return 1.0/n + harmonic_recursive(n-1)
georgegkas
  • 417
  • 6
  • 14
  • Is there some advantage to that recursive code over the iterative or approximation methods already shown? Recursion is sometimes more clear or natural than iteration, but not in this case, so its slow speed should be avoided. Your code seems to be useful for teaching recursion but not for answering the question--but I could be wrong. – Rory Daulton May 20 '17 at 11:32
  • @RoryDaulton true fact. But my code is not slower than an iterative one. I use linear recursion, that means the function body makes at most one new recursive call. The time an space efficiency is at most O(n). -I don't know if you'd like to write the proof.- – georgegkas May 20 '17 at 13:07
  • I used `timeit` on your function and an equivalent iterative one for `n=1000` in Python 3.6.1 and got 420 µs for your code and 140 µs for the iterative one. Function calls in general and recursion calls in particular in Python are expensive. I do use recursion in Python but not when iteration is equally clear. Of course, the time difference here is small in absolute terms so this is much ado about nothing unless n is very large. – Rory Daulton May 20 '17 at 14:26
  • @RoryDaulton good point. I measured it myself. This happens because each time a function is called (recursive or otherwise), a structure known as [activation record](https://www.cs.ucsb.edu/~pconrad/cs8/topics.beta/theStack/02/) or frame is created to store the state of the function. This is costly, especially for recursive functions. Thanks for point it out. – georgegkas May 20 '17 at 16:36
0

By using the numpy module, you can also alternatively use:

import numpy as np
def HN(n):
    return sum(1/arange(1,n+1))
Mostafa Ayaz
  • 480
  • 1
  • 7
  • 16