programming haskell chapter 11 切符番号選び

30
プププププププ Haskell 11 プ ププププププ @dekosuke

Upload: dekosuke

Post on 31-May-2015

1.227 views

Category:

Technology


3 download

DESCRIPTION

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

TRANSCRIPT

Page 1: Programming Haskell Chapter 11 切符番号選び

プログラミング Haskell 11 章

切符番号選び@dekosuke

Page 2: Programming Haskell Chapter 11 切符番号選び

2

数字遊び @ twitter切符番号選び切符番号選びの高速化

概略

Page 3: Programming Haskell Chapter 11 切符番号選び

3

数字遊び

ランダムな4つの数字から、 10 を作ろう!

Page 4: Programming Haskell Chapter 11 切符番号選び

4

数字遊び

Easy! => 3+2+7-2

Page 5: Programming Haskell Chapter 11 切符番号選び

5

数字遊び

TL を眺めてみると、問題によっては、難易度高い・・・

Page 6: Programming Haskell Chapter 11 切符番号選び

6

数字遊び

累乗で解決!

Page 7: Programming Haskell Chapter 11 切符番号選び

7

数字遊び

さらに難易度上昇

Page 8: Programming Haskell Chapter 11 切符番号選び

8

数字遊び

絶対値とルートで解決!?・・・

Page 9: Programming Haskell Chapter 11 切符番号選び

9

数字遊び

絶対値とルートで解決!?・・・

Page 10: Programming Haskell Chapter 11 切符番号選び

10

数字遊び

上の Twitter の数字遊びは、使える演算が明示されていないあいまいさがある(それが面白さにつながる点も)

使える演算をちゃんと決めよう!

Page 11: Programming Haskell Chapter 11 切符番号選び

11

切符番号選びのルール

正数のリストが与えられるリストから好きな数だけ選んで正数を使

い、四則演算を行える計算途中で0以下になってはいけない

Page 12: Programming Haskell Chapter 11 切符番号選び

12

切符番号選びのルール

[Int] -> [(Expr, Int)]

整数のリストから、四則演算で作られる式とその答えのリストが作られる欲しい数は、その中に「ある」か「ない」かのどちらか

Page 13: Programming Haskell Chapter 11 切符番号選び

13

切符番号選びのルール

というわけで、切符番号選びのプログラムを書きましょう!

Page 14: Programming Haskell Chapter 11 切符番号選び

14

切符番号選び

data Op = Add | Sub | Mul | Div-- 演算

valid :: Op -> Int -> Int -> Boolapply :: Op -> Int -> Int -> Int-- 演算が正当かどうかと、演算を行う関数--Maybe?...

Page 15: Programming Haskell Chapter 11 切符番号選び

15

切符番号選び

data Expr = Val Int | App Op Expr Expr-- 式

eval :: Expr -> [Int]-- 式の評価 ( 実質 Maybe)

-- 整数の組み合わせを作るため、次から色々関数を定義します--Val は適宜省略します

Page 16: Programming Haskell Chapter 11 切符番号選び

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]]

Page 17: Programming Haskell Chapter 11 切符番号選び

17

切符番号選び

perms :: [a] -> [[a]]perms [] = [[]]perms (x:xs) = concat (map (interleave x) (perms xs))

-- 集合の permutation( 並び替え )

Page 18: Programming Haskell Chapter 11 切符番号選び

18

切符番号選び

choices :: [a] -> [[a]]choices xs = concat (map perms (subs xs))

-- 全ての部分集合を含んだ順列を作る

Page 19: Programming Haskell Chapter 11 切符番号選び

19

切符番号選び

solution :: Expr -> [Int] -> Int -> Boolsolution e ns n = elem (values e) (choices ns) && eval e == [n]

-- 式 e を構成する数値がリスト ns に含まれている かつ 式 e を評価すると値 n になる

Page 20: Programming Haskell Chapter 11 切符番号選び

20

切符番号選び

全ての解答を探す関数 solutions を作りたい

solutions :: [Int] -> Int -> [Expr]--solutions ( 数のリスト ) 正解値 -> 数のリストが生成元の式で、評価すると正解値になるもの-- solutions [1,2,3] 3 = [Val 3, Add 1 2, Add 2 1]

Page 21: Programming Haskell Chapter 11 切符番号選び

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])]

Page 22: Programming Haskell Chapter 11 切符番号選び

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])]

Page 23: Programming Haskell Chapter 11 切符番号選び

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]

Page 24: Programming Haskell Chapter 11 切符番号選び

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]

-- 数値のリストを全て一回ずつ使った式すべてを返す ( 本当はこれは遅い ( 後述 ) )

Page 25: Programming Haskell Chapter 11 切符番号選び

25

完成

solutions :: [Int] -> Int -> [Expr]solutions ns n = [e|ns' <- choices ns, e<-exprs ns', eval e==[n]]-- 与えられた数を生成する式をすべて探す

が、遅い… (4 分 @ モダンなマシン )(Sub 1 2) <- こういう式が途中に出たら打ち切りたい

Page 26: Programming Haskell Chapter 11 切符番号選び

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 な演算すべてを適用

Page 27: Programming Haskell Chapter 11 切符番号選び

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)]

Page 28: Programming Haskell Chapter 11 切符番号選び

28

高速化

solutions' :: [Int] -> Int -> [Expr]solutions' ns n = [e|ns' <- choices ns, (e,m)<-results ns' , m==n]

-- 式を作るうえで valid でない演算は早めに打ち切る--10 数秒 @ モダンなマシン

Page 29: Programming Haskell Chapter 11 切符番号選び

29

高速化

さらに工夫を…・足し算や掛け算は可換なので  (Add 2 3) と (Add 3 2) は片方でいい・ (Mul x 1) は自明なので除去これらの条件を valid に入れ込む

Page 30: Programming Haskell Chapter 11 切符番号選び

30

高速化

・ solutions は元の数のリストに対して階乗で計算量が増大する・組合せ爆発・式の途中で計算を打ち切ると、組合せの増加速度を抑えられる→小さな工夫が指数的に効く