Life Goes On

まあまあだけど楽しんでる方です

24問目

http://projecteuler.net/index.php?section=problems&id=24
0から9までの数字で作る順列を辞書順に並べたときに、百万番目の順列を求める。
順列を作る部分はよくある問題みたいです。
http://www.sampou.org/cgi-bin/haskell.cgi?Programming%3a%b6%cc%bc%ea%c8%a2%3a%c1%c8%b9%e7%a4%bb#H-1ryeeo1

import Data.List

main = print $ (perm ['0'..'9']) !! 999999

perm :: String -> [String]
perm [] = [""]
perm digits = [x : xs | x <- digits, xs <- perm $ delete x digits]

と思ったら、「そんなんじゃ遅くて使いものにならねぇよ!」と言われてしまいました。(p_;)
2008-04-03 - HaHaHa! - haskell
は、速ぇ。
そっか、n個の数字の順列はn!種類だから、全部調べなくても計算すれば答えが出るんだ。という訳で修正したのが↓のコード。
ただこれにはまだ改善の余地があって、

  • divとmodではなくdivModを使う(商と剰余を求めるのに2回割り算をするのはムダ)
  • deleteではなくsplitAtを使う(インデックスが分かってるのに検索するのはムダ)
  • factの計算を1回だけにする(再利用できる)

といった工夫をするとリンク先のコードになるのだと思います。(あってるかな?)
あ、あとStringだけでなく一般的な配列に汎化してありますね。

import Data.List

main = print $ euler024 ['0'..'9'] 999999

euler024 :: String -> Int -> String
euler024 s n
    | ((length s) == 0) = ""
    | otherwise = q : (euler024 (delete q s) r)
    where
        r = mod n fact
        q = s !! (div n fact)
        fact = product [1..(length s)-1]