デジタル表示で 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秒くらいで求まりました。