Upload
hirosuga
View
3.280
Download
6
Embed Size (px)
DESCRIPTION
2012年5月19日に開催された、「第一回関数型言語勉強会 大阪」での発表資料です。コードとか:https://github.com/quassia88/InverseFizzBuzz/まとめとか:http://togetter.com/li/306278
Citation preview
Inverse FizzBuzz を解いてみよう!2012/5/19
@quassia88
自己紹介
Twitter: @quassia88
過去に、「Scalaで圏論入門」を翻訳https://github.com/scalajp/introduction-to-category-theory-in-scala-jp/wiki
Inverse FizzBuzz って何?
Krishnan Raman氏のブログ「just another scala quant」に5月3日付でアップロード。翌々週あたりから拡散。
原文http://www.jasq.org/2/post/2012/05/inverse-fizzbuzz.html
日本語訳http://d.hatena.ne.jp/matarillo/20120515/p1
コードゴルフhttp://golf.shinh.org/p.rb?Invert+FizzBuzz
Inverse FizzBuzz のあらすじ
時は2016年、関数型言語がプログラム言語界のメインストリームとなっていた。すでに、FizzBuzz問題はおばあちゃんでも解けるほどに陳腐化。Googleは採用試験に、Inverse FizzBuzzと呼ばれる最新の問題を出題しようとしていた・・・
毒舌な妹さんも安心です
Inverse FizzBuzz 問題の定義
問題:逆FizzBuzz問題
あるリストが与えられたときに、FizzBuzzを実行するとそのリストを出力するような最短の連続数列を求めよ。
なんのこっちゃ?
要は、FizzBuzzの逆問題!つまり、結果から原因を推定する問題。
FizzBuzz問題の復習:非負の整数列が与えられたとき、3の倍数ならばFizz、5の倍数ならばBuzz、3の倍数かつ5の倍数ならば、FizzBuzzを出力せよ。
FizzBuzzの例
数列 { 3, 4, 5, 6 } が与えられたとする。この数列にFizzBuzz を適用すると、
3 ⇒ Fizz4 ⇒5 ⇒ Buzz6 ⇒ Fizz
となり、文字列の列 { Fizz,Buzz,Fizz } がえられる。
Inverse FizzBuzzの例
では、文字列の列 { Fizz,Buzz,Fizz } が与えられたとする。この列と同じ列を出力する数列のうち、もっとも短いものは何か?
条件:● 解は一意ではないが、最短のものであればどれでもよい
Inverse FizzBuzzの例
先ほどのFizzBuzzの回答から、 { Fizz,Buzz,Fizz } を出力する数列は、
{ 3, 4, 5, 6 }
だと分かる。では、他の例は?
{ Fizz, Buzz } ⇒ { 3, 4, 5 } ではなく、{ 9, 10 }{ Buzz, Fizz } ⇒ { 5, 6 }{ Buzz, Buzz, Buzz } ⇒ 解なし!
再度 Inverse FizzBuzz の定義
Fizz,Buzz,FizzBuzz からなる、文字列の列が与えられたとする。FizzBuzz を適用した時、この列と同じ列を出力する数列のうち、もっとも短いものは何か?
条件:● 解は一意ではないが、最短のものであればどれでもよい。● 解が存在しない場合もある。何らかの対処を行うこと。
現在の心象
ノ´⌒`\ ∩___∩ ━┓ / γ⌒´ \ | ノ\ ヽ. ┏┛ / .// ""´ ⌒\ \ / ●゛ ● | ・ /. ___ ━┓ .i / \ ,_ i )\ | ∪ ( _●_) ミ / / ― \ ┏┛ i (・ )゛ ´( ・) i,/ \ 彡、 |∪| | / / (●) \ヽ ・ l u (__人_). | . \ / ∩ノ ⊃ ヽ / / (⌒ (●) /_\ ∩ノ ⊃ / ━┓\ ∧∧∧∧∧∧∧/ /  ̄ヽ__) /( \ / _ノ | |. ┏┛ \< > /´ ___/.\ “ /__| | ・ < ━┓ > | \―――――――――――――<. ┏┛ >――――――――――――― ___ ━┓ < ・ >. ____ ━┓ / ―\ ┏┛ < > / ― \ ┏┛ /ノ (●)\ ・ /∨∨∨∨∨∨\ /ノ ( ●) \ ・. | (●) ⌒)\ / \ | ( ●) ⌒) |. | (__ノ ̄ | / / ̄ ̄ヽ ━┓ \ | (__ノ ̄ / \ / / / (●) ..(● ┏┛ \ | / \ _ノ / | 'ー=‐' i ・ \ \_ ⊂ヽ∩\ /´ `\/ > く \ /´ (,_ \.\ | / _/ ,/⌒)、,ヽ_ \ | / \_ノ | / ヽ、_/~ヽ、__) \ \
どうするの?
● 整数列から出力される FizzBuzz 列は当然無限列。無限列に対して探索しろって?停止する保証は?● 見つからなかったら、永遠に探し続けるの?● 解が得られたとして、それが最短の数列であることはどうやって保証する?● それ以前に、FizzBuzz 列をどうやって数に戻すのよ?
困った時はどうしよう?
手段その1:力ずくで解く!有限の数列から得られた FizzBuzz 列の部分列をすべて列挙して、与えられた FizzBuzz 列と同じものがないか探す。
→ 出題者がもうやってます
手段その2:とにかく観察してみよう
FizzBuzz 列を図示してみた
観察すると・・・
図示すると分かること
これって循環してない?
Fizz, Buzz, Fizz, Fizz, Buzz, Fizz, FizzBuzz
がただ繰り返しているだけ?
考えてみりゃそりゃそーだ。倍数の出力を繰り返してるだけなんだから。(数論、群論、数学的帰納法なんかで証明できるかな・・・)
糸口を掴む
ある「有限の周期」で循環する列に対する探索の問題と考えればいいのでは?
循環列なので、十分な長さの列に対して探索を行えば
● 有限の時間で終了できることが保証できる● 解が得られたとして、それが最短の数列であることが保証できる● 解が得られなかった場合は、解がないことを保証できる
問題を分割してみる
● 循環する FizzBuzz 列を作成する● 循環する FizzBuzz 列と、与えられた FizzBuzz 列を比較し、一致する列を列挙する● FizzBuzz 列を数列に戻す● 数列のなかで、最短のものを選択する
もちろん、もっとエレガントな方法もあるはず!
でも、とりあえず上に従って分割した問題を解いてみる
分割された問題を解いてみよう
変形 Inverse FizzBuzz 問題1
ある非負整数 n から始まる数列があったとする。
この数列にFizzBuzz を適用した時、最初に Fizz/Buzz/FizzBuzz が出力される数は何か?
例: 3で始まる数列で、最初の Buzz を出力する数は?
変形 Inverse FizzBuzz 問題1回答例
/** * satisfies を満たす start から最も近い数を返す * start が satisfies を満たさなかった場合、 * next によって次の数が与えられる */def nearestNumber( satisfies:Int => Boolean, next:Int => Int, start:Int):Int = { if(satisfies(start)) start else nearestNumber(satisfies,next,next(start)) }
変形 Inverse FizzBuzz 問題1回答例
/** * FizzBuzz の逆写像を返す */def inverseMapOfFB(str:String):Int => Int = str match { case "fizz" => nearestNumber(x=> x%3==0 && x%5!=0 && x >= 3, _+1, _) case "buzz" => nearestNumber(x=> x%5==0 && x%3!=0 && x >= 5, _+1, _) case "fizzbuzz" => nearestNumber(x=> x%15==0 && x >= 15, _+1, _) }
inverseMapOfFB("buzz")(3) // 5が得られる
変形 Inverse FizzBuzz 問題2
ある長さのFizzBuzz列が与えられたとする。
このFizzBuzz列 を生成した数列が n から始まるのが分かっている時、FizzBuzz 列を数列に直せ
例: { Fizz, FizzBuzz } を出力する12で始まる数列は?
変形 Inverse FizzBuzz 問題2回答例
/** * 関数列 funcs に start を順に適用した数列を返す */ def chainSeq( start:Int, funcs:List[Int=>Int]):List[Int] = funcs match { case Nil => Nil case _ => funcs.head(start) :: chainSeq(funcs.head(start)+1,funcs.tail) }
chainSeq( 12, List("fizz","fizzbuzz").map(inverseMapOfFB) )
変形 Inverse FizzBuzz 問題2回答例
/** * 連続数列を返す */def contiguousSeq(xs:List[Int]):List[Int]=xs match { case Nil => Nil case x => (x.head to x.last).toList }
contiguousSeq(chainSeq( 12, List("fizz","fizzbuzz").map(inverseMapOfFB) ))
あとは自分で解いてみよう!
でも、注意事項を少々・・・
Inverse FizzBuzz の注意点
できた!と思ったら以下のことをテストしてみよう
● 最短になっているか?{ Fizz, Buzz } を与えた時に長さ2の列が返るか?
● 解のない FizzBuzz 列を与えた時、クラッシュしないか?
● 空の列(Nil)を与えた時、クラッシュしないか?正しい結果が返るか?(「解なし」じゃないよ!)
● 長いFizzBuzz列に対しても、解を与えるか?
Let’s solve Inverse FizzBuzz!
ご静聴ありがとうございました