著作一覧 |
TopCoderの最初の練習問題(チュートリアルではなくアリーナのほうのやつ)のアドバンスがどうやってもできずにえらく悩む。
1から与えられた数までの集合(たとえば、与えられた数が4なら、1,2,3,4)から、与えられた桁数を埋める数字を作る(たとえば与えられた数が3なら、123とか431とか)。
この時、作られた数字の並びが、左側に対して等しいか大きくなるような数は何通りできるか? という問題。たとえば、上の集合と桁であれば、123はOK、111もOK。でも、121はNG。
これは再帰を使えばすぐに解ける。といってもすぐにできたわけではないのが悲しいところであるが。
#最後のパラメータはデバッグ用 def count_r(start, range, depth, a) if depth == 0 p a if $DEBUG return 1 end r = 0 start.upto(range) do |is| a.push is r += count_r(is, range, depth - 1, a) a.pop end r end puts "4, 3(r)=" + count_r(1, 4, 3, []).to_s puts "4, 4(r)=" + count_r(1, 4, 4, []).to_s puts "4, 5(r)=" + count_r(1, 4, 5, []).to_s puts "93, 8(r)=" + count_r(1, 93, 8, []).to_s
最初の3つはすぐに求まる。
4, 3(r)=20 4, 4(r)=35 4, 5(r)=56
だが、4つ目(実際の問題のINDIGOのはず)は無限とも思われる時間がかかっても終わらない。もちろんYARVじゃなくてJavaで書いても同じだ。
ということは、もっと賢い(というか、全件を求めているのだから単に力任せで少しも賢くはないから、「もっと」というのは変だな)アルゴリズムがあるはずだ。
が、これがわからない。頭の中には左から右に枝分かれする絵があるのだが、再帰を使ってすべての枝をたどる方法ではなく、それを計算で求める方法がわからない。
で、考えていた。ほぼ1日。正味でも数時間かも。
さっきわかった。1から4の場合であれば
[1, 1, 1, 1] [4, 3, 2, 1] [10, 6, 3, 1] [20, 10, 4, 1]
ということか(数字自体は見えていたのだが、配列に入れることで求め方がわかったということ)。その要素から右端までの要素の総和が次ぎの要素となる。
それにしても、こういうの(あるいはもっと賢い方法)をさっと考えつく人がうじゃうじゃいるわけだから、世の中は広い。
いや、だから、今の、ステイブルなバージョンについて書くと(言葉は違うが)最初に書いてあるじゃん。あと、JRubyはスコープ外。
>特集1 が Java との比較ってのもすごいな
これは同意。正直、JRubyの記事が最初に来て、それに対しての補完(じゃないな。カウンターでもないし、蛇足と言う気はないし)になるのかと思ってた。
>Lisp 涙目
なぜ?
inabaさん(最初、間違えて書いてましたごめんなさい)の指摘に衝撃を受ける。
その通りだ。
やっていることは既に求めた枝の数の足し込みなのだから、キャッシュして利用すれば良い、ということもそうだが、それより僕が思いつかなかったのは、引数の組がそのまま枝の形(葉の数)だという点。これか。
すごく勉強になった。ありがとうございます。
晒してみるものだなぁ。
ジェズイットを見習え |
> Lisp 涙目<br>「lambdaをそう説明するか!」ということではないかと
難しいですねぇ。高杉晋作の説明するのに木戸孝允を出したら、吉田松陰が涙目という感じ?
更新ができないのは何故だ?
> [1, 1, 1, 1]<br>> [4, 3, 2, 1]<br>> [10, 6, 3, 1]<br>> [20, 10, 4, 1]<br>これは「パスカルの三角形」と呼ばれるものですね。
>これは「パスカルの三角形」と呼ばれるものですね。<br>あ、本当だ。気づきませんでした。ありがとうございます。<br>http://ja.wikipedia.org/wiki/%E3%83%91%E3%82%B9%E3%82%AB%E3%83%AB%E3%81%AE%E4%B8%89%E8%A7%92%E5%BD%A2