2

I need to define the list of the numbers whose only prime factors are 2, 3 and 5, the Hamming numbers. (I.e. numbers in the form of 2^i * 3^j * 5^k. The sequence starts with 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, …)

I may do it using the factors function, or otherwise. The factors below should return factors of its argument. I believe I’ve implemented it correctly.

   factors :: Int -> [Int]
   factors n = [x | x <- [1..(div n 2) ++ n], mod n x == 0]

I tried to make the list of 2^i * 3^j * 5^k using list comprehensions but got stuck on writing the guard:

hamming :: [Int]
hamming = [n | n <- [1..], „where n is a member of helper“]

helper :: [Int]
helper = [2^i * 3^j * 5^k | i <- [0..], j <- [0..], k <- [0..]]
Palec
  • 10,298
  • 7
  • 52
  • 116
john stamos
  • 961
  • 5
  • 15
  • 32
  • 1
    BTW, the `factors` function has a syntactic error. With the smallest change possible, it can be fixed as `factors n = [x | x – Palec Apr 27 '15 at 17:37

2 Answers2

11

I may do it using the factors function, or otherwise.

I recommend doing it otherwise.

One simple way is to implement a function getting the prime factorization of a number, and then you can have

isHamming :: Integer -> Bool
isHamming n = all (< 7) $ primeFactors n

which would then be used to filter the list of all positive integers:

hammingNumbers :: [Integer]
hammingNumbers = filter isHamming [1 .. ]

Another way, more efficient is to avoid the divisions and the filtering, and create a list of only the Hamming numbers.

One simple way is to use the fact that a number n is a Hamming number if and only if

  • n == 1, or
  • n == 2*k, where k is a Hamming number, or
  • n == 3*k, where k is a Hamming number, or
  • n == 5*k, where k is a Hamming number.

Then you can create the list of all Hamming numbers as

hammingNumbers :: [Integer]
hammingNumbers = 1 : mergeUnique (map (2*) hammingNumbers)
                                 (mergeUnique (map (3*) hammingNumbers)
                                              (map (5*) hammingNumbers))

where mergeUnique merges two sorted lists together, removing duplicates.

That’s already rather efficient, but it can be improved by avoiding producing duplicates from the beginning.

Community
  • 1
  • 1
Daniel Fischer
  • 174,737
  • 16
  • 293
  • 422
  • 1
    Actually, this `hammingNumbers` is equivalent to `[2^i*3^j*5^k | k – nymk Mar 23 '13 at 18:55
  • So how can we get it to creat all hamming numbers? – john stamos Mar 23 '13 at 20:47
  • @johnstamos Give it infinite time ;-) You can get them as `[2^i * 3^j * 5^k | n – Daniel Fischer Mar 23 '13 at 20:52
  • Great Thanks daniel. I have a couple questions about your code though. in isHamming what is "all (< 7) $ primeFactors n" doing? I haven't see the all function (i'm assuming <7 is a part of it), the $, and what is the function prime factors? (Believe I only have factors defined so far) And in hammingNumbers I haven't seen the union function, or * function yet. – john stamos Mar 23 '13 at 21:03
  • 1
    `all` is a function that takes a predicate and a list and checks whether all list elements satisfy the predicate, `all :: (a -> Bool) -> [a] -> Bool`; For prime factors, `(< 7)` checks that they are `2, 3` or `5`. `(*)` is simply multiplication, and `(2*)` is an operator section, so `(2*) = \x -> 2*x`. The functions `primeFactors` or `union` remain to be written (or copied from elsewhere). I suppose it is an exercise, and for you to learn from it, you have to do at least part yourself. If it's not an exercise, just go and take the code from the other question. – Daniel Fischer Mar 23 '13 at 21:17
  • Is there no way to be in infinite time and in order? – john stamos Mar 25 '13 at 01:23
  • @johnstamos The list-merging version creates them in ascending order. If you want to create a list `exponentTriples` such that `[2^i * 3^j * 5^k | (i,j,k) – Daniel Fischer Mar 25 '13 at 12:00
  • The `union` should not be confused with the `union` from Data.List. The latter expects unsorted finite lists, while this answer uses sorted infinite lists. A better name for the required function is `merge`, IMO. – Palec Apr 27 '15 at 17:30
  • @Palec The `merge` in mergesort doesn't remove duplicates, so that name isn't ideal either. I picked `union` thinking of sets, forgot about the existence of `Data.List.union`. I'm hesitant to use something clear but clumsy like `duplicateFreeMerge`. Have you an idea for a name that's clear but not clumsy? – Daniel Fischer Apr 27 '15 at 18:15
  • @DanielFischer Is `mergeUniq` OK? – Palec Apr 27 '15 at 19:45
  • @Palec Thanks. I decided to write "unique" out, however. – Daniel Fischer Apr 28 '15 at 18:59
2

Note that hamming set is

{2^i*3^j*5^k | (i, j, k) ∈ T}

where

T = {(i, j, k) | i ∈ [0..], j ∈ [0..], k ∈ [0..]}

But we can't use [(i, j, k) | i <- [0..], j <- [0..], k <- [0..]]. Because this list starts with an infinitely many triples like (0, 0, k).
Given any (i,j,k), elem (i,j,k) T should return True in finite time.
Sounds familiar? You can recall the question you asked before: haskell infinite list of incrementing pairs

In that question, hammar gave the answer for pairs. We can generalize it to triples.

triples = [(i,j,t-i-j)| t <- [0..], i <- [0..t], j <- [0..t-i]]
hamming = [2^i*3^j*5^k | (i,j,k) <- triples]
Community
  • 1
  • 1
nymk
  • 3,033
  • 3
  • 29
  • 35