海野秀之(うんのひでゆき)の外部記憶
Twitter (twilog) / RSS / アンテナ / ぶくま
このところ Google の検索結果にでなくなってしまっている 「水からの伝言」を信じないでください の冒頭に、以下のような文が追加されていた。
11 月 11 日前後から、このページが Google の検索にかからなくなっています。 これは、最近頻出している Google の技術的トラブルの可能性が高いとの ご指摘をご専門の方からいただきました。 要は、Google に頼りすぎてはいけないということでしょうね (11月14日)。
そういえば、最上嗣生さんがGoogle に ad-hoc なアルゴリズムを導入することで不安定になると言っていた のを思いだした。
(蛇足) 私は google の試行錯誤的なシステムが改変がいけないことだとは思っていません。 さっきの局所最適解と次の局所最適解の間は不安定でしょうから。 (←しまった、最上さんの主張をまったく理解していないように見えるなぁ。)
似たような出題を二回目撃してしまったため、 スルー力(するー・りょく)及ばず、変なフラグが立ってしまった。
いずれも 24 をターゲットにしているのは、切符問題にはなにか僕の知らない流儀でも?? さらに、解いてみると(と書くとさくっと解いたように聞こえるかも知れませんが、恥ずかしながら、 そんなことはありません *1) 、いずれの答えも同じ形の構文木になります。
むむー、この形が人間にとっては思いつきにくいということかな。
まだお二人のコードをきちんと読んでいませんが、 おそらく、それらとくらべて断然素人臭いと思われますが、拙作を以下に (ticket.rb)。
いやね、There is more than one way to do it が言い訳モットーでして。
次は scheme で書いてみよう。Shiro Kawai さんのコードを詳しくしらべるのはそれから。 (ざっと見た感じ、あんなコードは僕からは出てきそうにもない。非決定性なんちゃらって何だ? *2 )
#!/usr/local/bin/ruby
=begin
ticket.rb
いわゆる切符問題をときます。
「1, 3, 4, 6 の 4 つの数字(順序は入れ替えてよい)と四則演算をもちいて
24 をつくれ」
だったら、
./ticket.rb 24 1 3 4 6
になります。
=end
require 'rational'
=begin
== each_tree_permutaion(array) {|a| ... }
配列をうけとって、構文木の候補を生成します。
たとえば、[a, b, c] からは
[[a, b], c], [[b, a], c], [c, [a, b]], ...
が次々に生成されブロックに渡されます。
=end
def each_tree_permutation(ary)
pr = Proc.new
if ary.size > 2
0.upto(ary.size - 1) do |i0|
a = ary.dup
x = a.delete_at(i0)
0.upto(a.size - 1) do |i1|
b = a.dup
y = b.delete_at(i1)
0.upto(b.size) do |j|
each_tree_permutation(b.dup.insert(j, [x, y]), &pr)
end
end
end
else
pr.call(ary)
end
end
=begin
== each_operations(ary){|a| ... }
each_tree_permutation で生成された木の各ノード [x, y] に
可能な四則演算をはめこんでブロックに渡します。
[x, y] は、['+', x, y], ['-', x, y], ['*', x, y], ['/', x, y]
になります。y が 0 でないか等の検査はしない。
(eval まかせ)
=end
OPS=['+', '-', '*', '/']
def each_operations(ary)
x,y, = ary
def unexpanded?(a)
a.class == Array and a.size != 3
end
if unexpanded? x
each_operations(x) do |a|
if unexpanded? y
each_operations(y) do |b|
OPS.each do |op|
yield([op, a, b])
end
end
else
OPS.each do |op|
yield([op, a, y])
end
end
end
else
if unexpanded? y
each_operations(y) do |b|
OPS.each do |op|
yield([op, x, b])
end
end
else
OPS.each do |op|
yield([op, x, y])
end
end
end
end
=begin
== eval(expr) => [value, string]
each_operation が返す形式を受け取って、
値を評価します。
評価値と式の表現(文字列)の組を返します。
=end
def eval(expr)
op, x, y, = expr
def v_and_s(a)
if a.class == Array
eval(a)
else
[a, a.to_s]
end
end
x_val, x_str = v_and_s(x)
y_val, y_str = v_and_s(y)
ret_val = nil
if x_val and y_val
case(op)
when '+'
ret_val = x_val + y_val
when '-'
ret_val = x_val - y_val
when '*'
ret_val = x_val * y_val
when '/'
if y_val != 0
ret_val = x_val / y_val
else
ret_val = nil
end
end
end
ret_str = "(#{x_str} #{op} #{y_str})"
[ret_val, ret_str]
end
# ======== main ========
target = ARGV.shift.to_i
n = []
ARGV.each do |a|
n << Rational(a.to_i, 1)
end
hash = {}
each_tree_permutation(n) do |tree|
each_operations(tree) do |expr|
v,s = eval(expr)
if v == target
puts s unless hash[s]
hash[s] = true
end
end
end
書いてみて:
どれも、今回の問題とはあんまり関係ないうえに、「だから何?」的な。
しまったぁ、 Enumerable::each_with_index (昨日 tdiary のソースをみてて知った) 使えばよかった。(自分で書きながらも、0.upto(a.size - 1) はないだろうと)
案の定、あとから見ると恥ずかしいところがぞろぞろですね。
とりあえず、each operations なんで複数なのか、とか。コメントが……とか。 でも、書き直すんだったら一年後(←なぜ?