Dec 28 2007

I think my brain just exploded

Published by Brian at 7:27 pm under Functional Languages: Ocaml, Haskell

Haskell is making a real strong bid to wrestle the coveted title of “Brian’s Favorite Language” away from the current title holder (Ocaml, in case you haven’t guessed).

The thing that just happened was that I wanted to find some pythogorean triples- you know, groups of three numbers x, y, and z such that x2 + y2 = z2. So, without really knowing the language in any sort of deep way, and without thinking about it too much, I wrote the quick program:

f :: Int -> [ (Int, Int, Int) ]
f n = [ (x, y, z) |
x <- [ 1 .. n ],
y <- [ x+1 .. n ],
z <- [ y+1 .. n ],
(x * x) + (y * y) == (z * z) ]

You basically don’t even need to know a lick of Haskell to figure out what it does- anyone who’s gotten far enough in math to have encountered sets would probably guess correctly. Well, they might use the term “set” instead of “list”. The fact that it’s returning pythagorean triplets would be blatantly obvious. OK, so it’s an O(N3) algorithm, and you could probably do it in O(N2). When calling it with small numbers, who cares? I can find out that f 20 is [(3,4,5),(5,12,13),(6,8,10),(8,15,17),(9,12,15),(12,16,20)] so fast that I don’t see the computer pause, and that’s the answer I was looking for. And it’s hard to beat this code for succinctness and clarity.

Popularity: 41% [?]

16 Responses to “I think my brain just exploded”

  1. ChriSon 28 Dec 2007 at 9:15 pm

    List comprehensions are cool eh! They are available as well for ocaml as a Camlp4 extension (the [ a .. b ] notation is not available however, so one has to define range):

    # #directory “+camlp4″;;
    # #load “camlp4o.cma”;;
    Camlp4 Parsing version 3.10.0

    # #load “Camlp4Parsers/Camlp4ListComprehension.cmo”;;
    # let rec range a b = if a > b then [] else a :: range (a+1) b;;
    val range : int -> int -> int list =
    # let f n = [ (x,y,z) |
    x <- range 1 n;
    y <- range (x+1) n;
    z (int * int * int) list =
    # f 20;;
    - : (int * int * int) list =
    [(3, 4, 5); (5, 12, 13); (6, 8, 10); (8, 15, 17); (9, 12, 15); (12, 16, 20)]

  2. ChriSon 28 Dec 2007 at 9:22 pm

    The code of f did not quite appear es expected, let me try again:

    let f n = [ (x,y,z) |
    x <- range 1 n;
    y <- range (x+1) n;
    z <- range (y+1) n;
    x * x + y * y = z * z ]

  3. Robert Fischeron 28 Dec 2007 at 9:55 pm

    This is the kind of programming that I loved back in my Mathematica days…

  4. Drinkable Chicken » Cool listcompon 29 Dec 2007 at 8:07 am

    [...] this post, which demonstrates the power of Haskell’s list [...]

  5. Andreion 29 Dec 2007 at 11:58 am

    I like both Haskell and OCaml, but I have to say that it is unavoidable, for me, to think of Haskell as the more beautiful. The syntax, for starters, is far saner.

  6. Joeon 29 Dec 2007 at 2:36 pm

    I think haskell is definately a nicer language than ocaml. Doing stuff in ocaml often involves some rather ugly looking code, while haskell always seems to offer a consistent and elegant way to do anything. Just little things like completely removing the distinction between an operator and a function. In ocaml you can use an operator as a function, but you can’t use a function as an operator. Haskell lets you go both ways. And of course . and $ let you do away with the excessive parens that ocaml code requires. I also find doing imperative code in haskell much nicer than ocaml’s semicolon hell.

    Of course, ocaml has a much better implimentation than haskell does. GHC is the best you get with haskell (pretty much your only option really considering how many extensions and stuff are GHC only) and it can take a really long time to compile code at times. Ocaml has a fantastic implimentation, bytecode and native compilers that are very fast, lex and yacc, debugger, profiler, proper interactive mode, its all there. But GHCi is a lot better now than it was, and there’s on-going work to make ghc have fewer corner cases that make it grind to a halt. If alex, happy, hat and haddock became offical tools that came with GHC so you didn’t have to worry about 4 seperate possible version mismatches it would certainly make things nicer too.

  7. Jameson 29 Dec 2007 at 8:04 pm

    Funny, I just did this today for Project Euler (I’d already solved the problem using smarter methods than this, but I was curious as to how to do this with list comprehensions). While it was too slow for that problem, it’s great how a one-liner like this can do so much.

  8. ChriSon 29 Dec 2007 at 8:10 pm

    In passing, let me mention that the monadic version which looks quite similar too (even more so with the do notation not shown here):

    let f n =
    range 1 n >>= (fun x ->
    range (x+1) n >>= (fun y ->
    range (y+1) n >>= (fun z ->
    if x * x + y * y = z * z then return(x,y,z) else [] )))

    (the >>= and return are the standard monadic operators on lists — see http://enfranchisedmind.com/blog/2007/08/06/a-monad-tutorial-for-ocaml/).

  9. Justin Dubson 29 Dec 2007 at 8:20 pm

    Here’s a slight tweak of your haskell code to generate ALL of the pythagorean triples, in order of hypotenuse length:

    pythagoreanTriples :: [(Int, Int, Int)]
    pythagoreanTriples = [(a, b, c) | c <- [1..], b <- [1..c-1], a take 5 pythagoreanTriples
    [(3,4,5),(6,8,10),(5,12,13),(9,12,15),(8,15,17)]

  10. Justin Dubson 29 Dec 2007 at 8:24 pm

    Here is a slight tweak of your haskell code to make it generate ALL of the pythagorean triples, in order of hypotenuse length:

    pythagoreanTriples :: [(Int, Int, Int)]
    pythagoreanTriples = [(a, b, c) | c <- [1..], b <- [1..c-1], a <- [1..b-1], a^2 + b^2 == c^2]

    Prelude> take 5 pythagoreanTriples
    [(3,4,5),(6,8,10),(5,12,13),(9,12,15),(8,15,17)]

  11. sheon 29 Dec 2007 at 11:55 pm

    Hmm… unfortunately i am not good at math, but i hear more praise about haskell than ocaml. The question still remains however, aren’t both bad choice for people who are bad in math stuff?

  12. Spruce Mooseon 30 Dec 2007 at 3:43 pm

    Oh man - spoilers for Problem 9 of Project Euler or what. (-: I’m working on that one at the moment, but using Erlang.

  13. Brianon 30 Dec 2007 at 4:17 pm

    Programming is a bad choice for people who are bad in math stuff, IMHO.

  14. Brianon 30 Dec 2007 at 6:26 pm

    Spruce Moose: actually, I’m not working on Project Euler. This was something different.

  15. DavidLGon 31 Dec 2007 at 3:44 am

    Replying to the original post, you can do the same thing in Scala:

    def f(n:Int) = for(x <- 1 to n; y <- (x+1) to n; z <- (y+1) to n; if x*x + y*y == z*z) yield (x,y,z);

    The Scala compiler infers the return type of the function, Seq[(Int,Int,Int)], and compiles the code into statically-typed JVM bytecodes.

  16. kshitijon 26 Feb 2008 at 3:47 am

    for parselmouths:

    filter(lambda (x, y, z): x*x + y*y == z*z, [(a, b, c) for a in range (1, n+1) for b in range (a, n+1) for c in range(b, n+1)])

    obviously, the haskell and python versions are idea-wise isomorphic.

    and here’s a kind of O(n^2) version . the range for the multiplying factor (mu) is off the top of my head, and is there to take the output from the p-q stuff (eg (3,4,5)) and get the derived triplets (eg (6,8,10) and (12,16,20)). i say “kind of” O(n^2) because the generation of unique triplets is O(n^2) at minimum, and multiplying each of the integers in the triplet to get all the derived triplets is an additional operation. i don’t have the upper bound for the multiplying factor, though if it can be proved that such a bound exists and is independent of n, the algorithm would become strictly — as opposed to “kind of” — O(n^2)

    filter(lambda (a, b, c): max(a, b, c) <= n, [(p*q*mu, ((p**2 - q**2)/2)*mu, ((p**2 + q**2)/2)*mu) for q in range(1,n,2) for p in range(q+2,n,2) for mu in range(1, 5)])

Trackback URI | Comments RSS

Leave a Reply

Green Web Hosting! This site hosted by DreamHost.