デジタル表示で 0~9 の数字を表示させることを考えます。数字はディスプレイの 7つの場所の on, off で表示することになります。このとき、二つの数字を連続して表示するとして、7つの場所の on, off の切り替わりが出来るだけ少なくなるような順番を考えます。つまりその場所で、できるだけ on なら on、off なら off のままになるようにするということです。
0~9 の数字を適当な順番ですべて一度ずつ表示させるとき、on, off の切り替え数の和の最小値を求めよという問題です。
q38.rb
pattern = [0b1111110, 0b0110000, 0b1101101, 0b1111001, 0b0110011, 0b1011011, 0b1011111, 0b1110000, 0b1111111, 0b1111011] @table = {} [*0..9].combination(2) do |i, j| @table[[i, j]] = ("%07b" % (pattern[i] ^ pattern[j])).count("1") end @min = Float::INFINITY def count(not_used, co, order) i = order[-1] not_used -= [i] if not_used.empty? @min = co p [order, co] end not_used.each do |j| pair = (i < j) ? [i, j] : [j, i] sum = co + @table[pair] next if sum >= @min count(not_used, sum, order + [j]) end end count([*0..9], 0, [0]) puts @min
結果。
$ time ruby q38.rb [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 28] [[0, 1, 2, 3, 4, 5, 6, 7, 9, 8], 27] [[0, 1, 2, 3, 4, 5, 6, 8, 7, 9], 26] [[0, 1, 2, 3, 4, 5, 6, 8, 9, 7], 23] [[0, 1, 2, 3, 5, 6, 8, 9, 4, 7], 21] [[0, 1, 2, 8, 6, 5, 9, 3, 7, 4], 20] [[0, 1, 3, 2, 8, 6, 5, 9, 4, 7], 19] [[0, 1, 4, 7, 3, 2, 8, 6, 5, 9], 18] [[0, 1, 4, 7, 3, 9, 5, 6, 8, 2], 17] [[0, 1, 7, 3, 2, 8, 6, 5, 9, 4], 16] [[0, 2, 3, 5, 6, 8, 9, 4, 1, 7], 15] [[0, 2, 8, 6, 5, 9, 3, 7, 1, 4], 14] [[0, 8, 6, 5, 9, 4, 1, 7, 3, 2], 13] 13 real 0m0.611s user 0m0.608s sys 0m0.000s
最小値は 13 になります。まず、すべての組の数字の on, off の切り替え数を先に計算しておいて、@table に格納します。あとはすべての順列の場合を再帰で求めています。その際、途中で和が暫定的な最小値 @min の値に等しくなるかそれを超えた場合、それ以上の探索は無用なので打ち切って高速化します。自分の環境では 0.6秒くらいで求まりました。
プログラマ脳を鍛える数学パズル シンプルで高速なコードが書けるようになる70問
- 作者: 増井敏克
- 出版社/メーカー: 翔泳社
- 発売日: 2015/10/16
- メディア: Kindle版
- この商品を含むブログ (8件) を見る