Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Programming Haskell Chapter 11 切符番号選び

1,775 views

Published on

Programming Haskell Chapter 11 切符番号選びについて

Published in: Technology
  • Be the first to comment

Programming Haskell Chapter 11 切符番号選び

  1. 1. プログラミングHaskell 11章切符番号選び @dekosuke
  2. 2. 概略 数字遊び @ twitter 切符番号選び 切符番号選びの高速化 2
  3. 3. 数字遊び ランダムな4つの数字から、10を作ろう! 3
  4. 4. 数字遊び Easy! => 3+2+7-2 4
  5. 5. 数字遊びTLを眺めてみると、問題によっては、難易度高い・・・ 5
  6. 6. 数字遊び 累乗で解決! 6
  7. 7. 数字遊びさらに難易度上昇 7
  8. 8. 数字遊び 絶対値とルートで解決!?・・・ 8
  9. 9. 数字遊び絶対値とルートで解決!?・・・ 9
  10. 10. 数字遊び 上のTwitterの数字遊びは、使える演算が明 示されていないあいまいさがある(それ が面白さにつながる点も) 使える演算をちゃんと決めよう! 10
  11. 11. 切符番号選びのルール 正数のリストが与えられる リストから好きな数だけ選んで正数を使 い、四則演算を行える 計算途中で0以下になってはいけない 11
  12. 12. 切符番号選びのルール[Int] -> [(Expr, Int)]整数のリストから、四則演算で作られる式とその答えのリストが作られる欲しい数は、その中に「ある」か「ない」かのどちらか 12
  13. 13. 切符番号選びのルールというわけで、切符番号選びのプログラムを書きましょう! 13
  14. 14. 切符番号選びdata Op = Add | Sub | Mul | Div--演算valid :: Op -> Int -> Int -> Boolapply :: Op -> Int -> Int -> Int--演算が正当かどうかと、演算を行う関数--Maybe?... 14
  15. 15. 切符番号選びdata Expr = Val Int | App Op Expr Expr--式eval :: Expr -> [Int]--式の評価 (実質Maybe)--整数の組み合わせを作るため、次から色々関数を定義します--Val は適宜省略します 15
  16. 16. 切符番号選びsubs :: [a] -> [[a]]subs [] = [[]]subs (x:xs) = yss ++ map (x:) yss where yss = subs xs--リストの全ての部分集合を作る--集合のべき乗(2^X)--subs [1, 2] = [[], [2], [1], [1,2]] 16
  17. 17. 切符番号選びperms :: [a] -> [[a]]perms [] = [[]]perms (x:xs) = concat (map (interleave x)(perms xs))--集合のpermutation(並び替え) 17
  18. 18. 切符番号選びchoices :: [a] -> [[a]]choices xs = concat (map perms (subs xs))--全ての部分集合を含んだ順列を作る 18
  19. 19. 切符番号選びsolution :: Expr -> [Int] -> Int -> Boolsolution e ns n = elem (values e) (choices ns)&& eval e == [n]--式eを構成する数値がリストnsに含まれている かつ 式eを評価すると値nになる 19
  20. 20. 切符番号選び全ての解答を探す関数solutionsを作りたいsolutions :: [Int] -> Int -> [Expr]--solutions (数のリスト) 正解値-> 数のリストが生成元の式で、評価すると正解値になるもの-- solutions [1,2,3] 3 = [Val 3, Add 1 2, Add 2 1] 20
  21. 21. 切符番号選びsplit :: [a] -> [([a],[a])]split [] = []split [_] = []split (x:xs) = ([x],xs):[(x:ls,rs)|(ls,rs) <- split xs]--集合を全ての場所で2つに割る-- split [1,2,3,4] = [([1],[2,3,4]), ([1,2],[3,4]),([1,2,3],[4])] 21
  22. 22. 切符番号選びsplit :: [a] -> [([a],[a])]split [] = []split [_] = []split (x:xs) = ([x],xs):[(x:ls,rs)|(ls,rs) <- split xs]--集合を全ての場所で2つに割る-- split [1,2,3,4] = [([1],[2,3,4]), ([1,2],[3,4]),([1,2,3],[4])] 22
  23. 23. 切符番号選びcombine :: Expr -> Expr -> [Expr]combine l r = [App o l r | o<-ops]ops :: [Op]ops = [Add,Sub,Mul,Div]--2つの式にすべての演算を適用--[Add 式1 式2, Sub 式1 式2, Mul 式1 式2, Div式1 式2] 23
  24. 24. 切符番号選びexprs :: [Int] -> [Expr]exprs [n] = [Val n]exprs ns = [e | (ls, rs) <- split ns, l<-exprs ls, r<-exprs rs, e<-combine l r]--数値のリストを全て一回ずつ使った式すべてを返す (本当はこれは遅い(後述) ) 24
  25. 25. 完成solutions :: [Int] -> Int -> [Expr]solutions ns n = [e|ns <- choices ns, e<-exprsns, eval e==[n]]--与えられた数を生成する式をすべて探すが、遅い… (4分 @ モダンなマシン)(Sub 1 2) <- こういう式が途中に出たら打ち切りたい 25
  26. 26. 高速化type Result = (Expr, Int)combine :: Result -> Result -> [Result]combine (l,x) (r,y) = [(App o l r, apply o x y)|o <-ops, valid o x y]--2つのExprに対して、validな演算すべてを適用 26
  27. 27. 高速化results :: [Int] -> [Result]results [] = []results [n] = [(Val n, n) | n>0 ]results ns = [res | (ls,rs) <- split ns, lx<-results ls,ry <-results rs, res<-combine lx ry]--数値のリストを、すべて使った式を作って評価する-- [1,2] -> [(Add 1 2, 3), (Mul 1 2, 2)] 27
  28. 28. 高速化solutions :: [Int] -> Int -> [Expr]solutions ns n = [e|ns <- choices ns, (e,m)<-results ns , m==n]--式を作るうえでvalidでない演算は早めに打ち切る--10数秒 @ モダンなマシン 28
  29. 29. 高速化さらに工夫を…・足し算や掛け算は可換なので (Add 2 3) と (Add 3 2)は片方でいい・(Mul x 1) は自明なので除去これらの条件をvalidに入れ込む 29
  30. 30. 高速化・solutionsは元の数のリストに対して階乗で計算量が増大する・組合せ爆発・式の途中で計算を打ち切ると、組合せの増加速度を抑えられる→小さな工夫が指数的に効く 30

×