Seven Trees

id:bonotake さんに教えてもらった問題。すごく面白い。
ref: http://d.hatena.ne.jp/m-hiyama/20081022/1224640248


この問題を Haskell で言うと、

data Tree = Leaf | Node Tree Tree deriving Eq
type Tree7 = (Tree, Tree, Tree, Tree, Tree, Tree, Tree)

で定義される普通の二分木と、二分木の 7 つ組を考える。このとき

f :: Tree7 -> Tree
g :: Tree -> Tree7

の型になる 2 つの関数 f と g を定義せよ。ただし以下の 3 つの条件が満たされるものとする。

  • Tree 型の任意の値 x に対して x == f (g x)
  • Tree7 型の任意の値 x に対して x == g (f x)
  • f と g はともに O(1)

つまり Tree と Tree7 の間の全単射を定義せよ、という問題 *1 *2

Tree7 というところが重要で、Tree2 〜 Tree6 だとできない (っぽい) というのがとても面白い。そしてその直感的な証明がものすごく面白い。まあこの辺の話は全部 id:bonotake さんに教えてもらったのだけど。たぶん自分では解けないしー。

で、プログラムを書いてみました。QuickCheck は通った感じです。

import Control.Monad
import Test.QuickCheck

data Tree = Leaf | Node Tree Tree deriving (Eq, Show)
type Tree7 = (Tree, Tree, Tree, Tree, Tree, Tree, Tree)

-- Tree の生成法を定義する (QuickCheck 用)
instance Arbitrary Tree where
    arbitrary = oneof [ return Leaf, liftM2 Node arbitrary arbitrary ]

-- 7 つ組の生成法を定義する (QuickCheck 用)
instance
  (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d,
   Arbitrary e, Arbitrary f, Arbitrary g) =>
   Arbitrary (a, b, c, d, e, f, g) where
    arbitrary =
      return (,,,,,,)
        `ap` arbitrary `ap` arbitrary `ap` arbitrary `ap` arbitrary
        `ap` arbitrary `ap` arbitrary `ap` arbitrary

f :: Tree7 -> Tree
-- (ひみつ)

g :: Tree -> Tree7
-- (ひみつ)

-- QuickCheck で検査する
main :: IO ()
main = do
    quickCheck $ \x -> x == f (g x)
    quickCheck $ \x -> x == g (f x)
$ ghc --make seven-trees.hs
$ ./seven-trees
OK, passed 100 tests.
OK, passed 100 tests.

解答や解説は檜山さんの後で。


追記:
再帰しちゃダメという条件が抜けていたので、計算量の条件を付け足してみました。不備があったら教えてください。

*1:ぼくの理解が正しければ。空の木とか言うのがよくわかってないので勘違いしてるかもしれない。

*2:言うまでもないですが、unsafePerformIO のような裏技を使う問題ではないです。