How to resolve the algorithm Hamming numbers step by step in the Python programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm Hamming numbers step by step in the Python programming language

Table of Contents

Problem Statement

Hamming numbers are numbers of the form   Hamming numbers   are also known as   ugly numbers   and also   5-smooth numbers   (numbers whose prime divisors are less or equal to 5).

Generate the sequence of Hamming numbers, in increasing order.   In particular:

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Hamming numbers step by step in the Python programming language

The provided code contains several functions that generate Hamming numbers. Hamming numbers are defined as numbers that can be expressed as a product of powers of 2, 3, and 5.

hamming2() function:

  • Uses a memoization technique to store previously calculated Hamming numbers.
  • Maintains a list of multipliers (2, 3, 5) and corresponding indices into the memoization list.
  • Generates Hamming numbers by finding the minimum value among the multiplied values of the current indices.
  • Updates the indices and values as it generates new Hamming numbers.

hamming() function:

  • Calculates Hamming numbers up to a specified limit by iteratively multiplying and checking for the minimum value.
  • Uses a list to store the generated Hamming numbers.

h() function:

  • Generates Hamming numbers using a heap.
  • Initially, the heap contains a single value (1).
  • It pops the smallest value from the heap and pushes its multiples of 2, 3, and 5 back into the heap.
  • Duplicates are eliminated by popping all equal values from the heap before pushing new multiples.

raymonds_hamming() function:

  • Uses a generator function to yield Hamming numbers.
  • Creates three generator functions for multiples of 2, 3, and 5.
  • Merges these generators and filters out duplicates to produce a sequence of Hamming numbers.

hamming_numbers() function:

  • Generates Hamming numbers using tee() to create multiple iterators from the same generator.
  • Merges three iterators that produce multiples of 2, 3, and 5.

hamming() function (different from the one above):

  • Generates Hamming numbers using merge() and chain() from itertools.
  • Defines helper functions to generate sequences of multiples of 2, 3, and 5.
  • Merges these sequences and skips duplicates to produce a sequence of Hamming numbers.

All the functions produce sequences of Hamming numbers, and the code demonstrates how to generate the first 20 numbers, the 1691st number, and the 1 millionth number using different approaches.

Source code in the python programming language

from itertools import islice

def hamming2():
    '''\
    This version is based on a snippet from:
        https://web.archive.org/web/20081219014725/http://dobbscodetalk.com:80
                         /index.php?option=com_content&task=view&id=913&Itemid=85
        http://www.drdobbs.com/architecture-and-design/hamming-problem/228700538
        Hamming problem
        Written by Will Ness
        December 07, 2008

        When expressed in some imaginary pseudo-C with automatic
        unlimited storage allocation and BIGNUM arithmetics, it can be
        expressed as:
            hamming = h where
              array h;
              n=0; h[0]=1; i=0; j=0; k=0;
              x2=2*h[ i ]; x3=3*h[j]; x5=5*h[k];
              repeat:
                h[++n] = min(x2,x3,x5);
                if (x2==h[n]) { x2=2*h[++i]; }
                if (x3==h[n]) { x3=3*h[++j]; }
                if (x5==h[n]) { x5=5*h[++k]; } 
    '''
    h = 1
    _h=[h]    # memoized
    multipliers  = (2, 3, 5)
    multindeces  = [0 for i in multipliers] # index into _h for multipliers
    multvalues   = [x * _h[i] for x,i in zip(multipliers, multindeces)]
    yield h
    while True:
        h = min(multvalues)
        _h.append(h)
        for (n,(v,x,i)) in enumerate(zip(multvalues, multipliers, multindeces)):
            if v == h:
                i += 1
                multindeces[n] = i
                multvalues[n]  = x * _h[i]
        # cap the memoization
        mini = min(multindeces)
        if mini >= 1000:
            del _h[:mini]
            multindeces = [i - mini for i in multindeces]
        #
        yield h

import psyco

def hamming(limit):
    h = [1] * limit
    x2, x3, x5 = 2, 3, 5
    i = j = k = 0

    for n in xrange(1, limit):
        h[n] = min(x2, x3, x5)
        if x2 == h[n]:
            i += 1
            x2 = 2 * h[i]
        if x3 == h[n]:
            j += 1
            x3 = 3 * h[j]
        if x5 == h[n]:
            k += 1
            x5 = 5 * h[k]

    return h[-1]

psyco.bind(hamming)
print [hamming(i) for i in xrange(1, 21)]
print hamming(1691)
print hamming(1000000)

from heapq import heappush, heappop
from itertools import islice

def h():
    heap = [1]
    while True:
        h = heappop(heap)
        while heap and h==heap[0]:
            heappop(heap)
        for m in [2,3,5]:
            heappush(heap, m*h)
        yield h

print list(islice(h(), 20))
print list(islice(h(), 1690, 1691))
print list(islice(h(), 999999, 1000000)) # runtime 9.5 sec on i5-3570S

from itertools import tee, chain, groupby, islice
from heapq import merge

def raymonds_hamming():
    # Generate "5-smooth" numbers, also called "Hamming numbers"
    # or "Regular numbers".  See: http://en.wikipedia.org/wiki/Regular_number
    # Finds solutions to 2**i * 3**j * 5**k  for some integers i, j, and k.

    def deferred_output():
        for i in output:
            yield i

    result, p2, p3, p5 = tee(deferred_output(), 4)
    m2 = (2*x for x in p2)                          # multiples of 2
    m3 = (3*x for x in p3)                          # multiples of 3
    m5 = (5*x for x in p5)                          # multiples of 5
    merged = merge(m2, m3, m5)
    combined = chain([1], merged)                   # prepend a starting point
    output = (k for k,g in groupby(combined))       # eliminate duplicates

    return result

print list(islice(raymonds_hamming(), 20))
print islice(raymonds_hamming(), 1689, 1690).next()
print islice(raymonds_hamming(), 999999, 1000000).next()

from heapq import merge
from itertools import tee

def hamming_numbers():
    last = 1
    yield last

    a,b,c = tee(hamming_numbers(), 3)

    for n in merge((2*i for i in a), (3*i for i in b), (5*i for i in c)):
        if n != last:
            yield n
            last = n

from itertools import islice, chain, tee

def merge(r, s):
    # This is faster than heapq.merge.
    rr = r.next()
    ss = s.next()
    while True:
        if rr < ss:
            yield rr
            rr = r.next()
        else:
            yield ss
            ss = s.next()

def p(n):
    def gen():
        x = n
        while True:
            yield x
            x *= n
    return gen()

def pp(n, s):
    def gen():
        for x in (merge(s, chain([n], (n * y for y in fb)))):
            yield x
    r, fb = tee(gen())
    return r

def hamming(a, b = None):
    if not b:
        b = a + 1
    seq = (chain([1], pp(5, pp(3, p(2)))))
    return list(islice(seq, a - 1, b - 1))

print hamming(1, 21)
print hamming(1691)[0]
print hamming(1000000)[0]

  

You may also check:How to resolve the algorithm Bulls and cows step by step in the Scheme programming language
You may also check:How to resolve the algorithm Permutation test step by step in the FreeBASIC programming language
You may also check:How to resolve the algorithm Loops/Downward for step by step in the Lhogho programming language
You may also check:How to resolve the algorithm Empty program step by step in the Erlang programming language
You may also check:How to resolve the algorithm User input/Text step by step in the Plain English programming language