Saturday, December 29, 2007

Consul The Educated Monkey

Consul, the Educated Monkey.

                  1
                12  4
              11  24  9
            10  22  36  16
           9  20  33  48  25
         8  18  30  44  60  36
       7  16  27  40  55  72  49
     6  14  24  36  50  66  84  64
   5  12  21  32  45  60  77  96   81
  4 10  18  28  40  54  70  88  108   100
 3 8  15  24  35  48  63  80  99   120   121
2 6 12  20  30  42  56  72  90  110   132   144
  • Input: n
  • Output: combination(n+1, 2) numbers in an (equilateral) triangle

For example, when n = 12, generate combination(13, 2) = 13!/(2!11!) = 78 numbers.

n = 1, combination(2,2) = 1.

1

n = 2, combination(3,2) = 3.

 1
2 4

n = 3, combination(4,2) = 6.

  1
 3 4
2 6 9

n = 4, combination(5,2) = 10.

   1
  4 4
 3 8  9
2 6 12 16

See the pattern?

    1^2
   4    2^2
 3   4*2   3^2
2 3*2   4*3   4^2

Rotating the triangle counter clockwise might reveal something:

1^2   2^2   3^3   4^2
   4*1   4*2   4*3
      3*1   3*2
         2*1

In general, for input n,

1^2   2^2 ..................................... n^2
   n*1       n*2 ....................... n*(n-1)
      (n-1)*1   (n-1)*2 ... (n-1)*(n-1-1)
             .....................
                      2

Let's implement it in Haskell, an obscure language.

module Main where

First, declare a module.

import qualified System.IO as Sys
import qualified IO

Then, import stuff needed.

main :: IO ()
main = do
    IO.hSetBuffering IO.stdout IO.NoBuffering
    Sys.putStr "Enter n (>=1): "
    num <- Sys.getLine
    let n :: Integer
        n = read num
    print' $ consul n (*)

main function definition. It disables stdout buffering so that the prompt is displayed immediately without waiting for the buffer to be flushed (probably flushed when new line is printed). Then it reads an integer from user and prints the triangle.

consul :: (Integral a) => a -> (a -> a -> a) -> [[a]]
consul n func = (map reverse . diagonals) $ consul' n func

consul just calls consul' then applies diagonals to the output. Then, it applies reverse on each element of the output.

consul' :: Integral a => a -> (a -> a -> a) -> [[a]]
consul' n func = [map (^2) [1..n]]
    ++ [zipWith func (repeat x) [1..x-1] | x <- [n,n-1..2]]

consul' constructs the upside down triangle described above. The triangle is expressed as a list of list of integers.

[[1^2,   2^2, ....................................., n^2]
   , [n*1,       n*2, ......................., n*(n-1)]
      , [(n-1)*1,   (n-1)*2, ..., (n-1)*(n-1-1)]
             , .....................
                      , [2]]

diagonals :: [[a]] -> [[a]]
diagonals [[]] = [[]]
diagonals ([]:ls) = diagonals ls
diagonals ((x:xs):ls) = [x] : zipWith (:) xs (diagonals ls)

diagonals takes elements along the diagonal (NE to SW). Essentially, it rotates the upside triangle clockwise and flips. Since the output triangle is flipped, reverse should be applied for each element list to re-flip the triangle.

print' :: (Show a) => [[a]] -> IO ()
print' [] = return ()
print' (l:ls) = do
    putStrLn $ replicate (length ls) ' ' ++ show l
    print' ls

print' prints the list of list of integers as a triangle by appending spaces in front of each row.

Thursday, December 27, 2007

Literate Haskell with Pandoc (Markdown)

This is Hello.lhs. It uses Markdown.

First, declare module name:

module Main where

Second, define main function:

main :: IO ()

main is of type IO ().

main = do
    putStrLn "Hello, World!"

That's the entire hello world in Haskell. To compile this,

ghc Hello.lhs

To convert this into HTML,

sed -e 's/^> /    /' Hello.lhs | pandoc

KTHXBYE.

Thursday, December 20, 2007