2012年12月第4回granpark.rb 「山本山の野望」
12月の勉強会で出した課題と回答。
1問目は(1)回文判定と、(2)与えられた文に+αして最短の回文にするっていう内容。
(2)をいろいろな書き方をする人が多くて面白かった。
2問目は与えられたマップから島(コロニー)の数を数える、という問題。
勉強会の時間内では1問目でタイムアップのチームが多かったけど、
勉強会時間外のタイミングで解いてきてくれた人もいました。
解答例はDFS(深さ優先探索)です。
AOJ0052 Factorial II
例によってRubyで書いた。
末尾に0がつく===10が掛かっている===2と5がかかっている、だから
素因数の2と5の少ない方が0の個数になる、という方針で解いた。
以下ソースと、テスト。
# encoding: utf-8 class ZeroCounter attr_accessor:num attr_accessor:num_zero def initialize(i) @num = i @num_zero = [count_num_of_primes(2), count_num_of_primes(5)].min p @num_zero end def count_num_of_primes(prime) num_pf = 0 i = prime while @num / i > 0 do num_pf += @num / i i = i * prime end return num_pf end end while (line = gets) do if line.to_i != 0 then ZeroCounter.new(line.to_i) end end
#encoding: utf-8 require 'rspec' require './a' describe ZeroCounter do it "should return the num of prime factor 2" do zc = ZeroCounter.new(19) zc.count_num_of_primes(2).should == 16 end it "should return the num of zeros of factorial" do zc = ZeroCounter.new(19) zc.num_zero.should == 3 end it "should return the num of zeros of factorial" do zc = ZeroCounter.new(45) zc.num_zero.should == 10 end it "should return the num of zeros of factorial" do zc = ZeroCounter.new(50) zc.num_zero.should == 12 end end
AOJ0067 The Number of Island
問題
ほとんどこれと同じコードで通るんじゃないかとおもって
入力部分とneighbor返すところだけ少し手直ししたら通った。
入力部分がなんか汚いのはご愛嬌。
最後に空行があるのかないのかわからなかったので両方いけるようにした。
class IslandCounter attr_accessor :map def initialize(map) @map = map end def neighbors(y, x) w = map[0].length h = map.length neighbor_list =[[y-1, x], [y, x-1], [y, x+1], [y+1, x]] return neighbor_list.select{|i| i[0] >= 0 && i[0]<h && i[1] >=0 && i[1]<w }.select{|i| @map[i[0]][i[1]] == 1 } end def num_island_color @map.flatten.sort.uniq.select{|i|i!=0}.length end def explorer_all_islands num_islands = 0 while (coord = find_new_island) != nil do num_islands += 1 explorer_in_an_island(coord[0], coord[1]) end return num_islands end def explorer_in_an_island(y, x) @map[y][x] = 0 neighbors(y, x).each{|coords| explorer_in_an_island(coords[0], coords[1]) } end def find_new_island if @map.flatten.include?(1) then h = @map.length w = @map[0].length place = @map.flatten.index(1) return [place / w, place % w] else return nil end end end current_map = Array.new while (line = gets) do line = line.chomp if line != "" then current_map.push line.split("").map{|i| i.to_i} else if current_map.size > 1 then ic = IslandCounter.new(current_map) p ic.explorer_all_islands current_map = Array.new end end end if current_map.size > 1 then ic = IslandCounter.new(current_map) p ic.explorer_all_islands end
AOJ1160 How Many Islands?
Rubyで書いてみた。
class IslandCounter
attr_accessor :map
def initialize(map)
@map = map
end
def neighbors(y, x)
w = map[0].length
h = map.length
neighbor_list =[[y-1, x-1], [y-1, x], [y-1, x+1], [y, x-1], [y, x+1], [y+1, x-1], [y+1, x], [y+1, x+1]]
return neighbor_list.select{|i| i[0] >= 0 && i[0]=0 && i[1]
Inputに対するOutputは正しそうな感じなんだけれども、
50*50で1を敷き詰めるInputをあたえたら、stack level too deep (SystemStackError)だった。
アウトプットするとあと一行かそこらで数え切れそうなんだけど惜しい感じ。
もうちょっと効率化したらRubyでもいけそうなんだけど…
というわけで上記コードは未Acceptです。
Rubyの再帰の限界を調べてみる
深さ優先探索の問題(http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=1160)を
解いていたら手元のサンプルインプットではうまく動くのだけど、
Online Judgeに投げ込むとうまく動かない。(Runtime Errorとのこと)
ためしに問題の上限サイズで入力例を作ったら、
a.rb:11: stack level too deep (SystemStackError)
みたいなエラーがでたので再帰が深すぎてエラーに成ってるのではないか。
def func(n)
if n == 1 then
return "hoge"
elsif n > 1
#p n
func(n-1)
end
end
p func ARGV[0].to_i
っていうスクリプトを作っていろいろ引数を変えて動かしてみる。
ruby --version
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
# ruby stacktest.rb 8735
だと
stacktest.rb:2: stack level too deep (SystemStackError)
# ruby stacktest.rb 8734
だと
"hoge"
と表示されるので、このコード・環境での再帰の限界は8734回までということになる。
ruby --version
ruby 1.8.7 (2012-06-29 patchlevel 370) [x86_64-linux]
に切り替えると、
# ruby stacktest.rb 5727
"hoge"
# ruby stacktest.rb 5728
stacktest.rb:6:in `func': stack level too deep (SystemStackError)
from stacktest.rb:6:in `func'
from stacktest.rb:9
になるので、5727回まで。
また1.9.3に戻して、
def func(n)
if n == 1 then
return "hoge"
elsif n > 1
arr = Array.new [" "]
return func(n-1)
end
end
p func ARGV[0].to_i
っていう形で関数が余計なarrを保持するようにすると、8188回までで打ち止め。
arrを削ったら8734回まで行けるようになった。
というわけで、
- 呼び出し元メソッドが確保している変数などの領域を増やすと
スタック呼び出し可能な深さが減る
- RubyのVersionによってもスタック呼び出し可能な深さが減る
ということは分かった...けど直接的に呼び出し可能回数をもっと増やすにはどうしたらいいかは
よくわからないままです(オチなし)
-
-
-
- -
-
-
追記: コメントでRuby2.0以降では、環境変数にRUBY_THREAD_VM_STACK_SIZEを設定することでスタックサイズが設定できると情報をいただきました。
謝辞
元データは青空文庫様にお借りしました。
青空文庫・作家別ページ「石川啄木」
青空文庫トップページ
Rubyのコミュニティの皆様、Rubyとても便利です。これからも使わせていただきます。
また、Botのアイコンには日本文学館様の『一握の砂 他』の端正な印象の書影をお借りしております。
そして素晴らしく心に刺さる数々の詩を残してくださった石川啄木さん(1886 - 1912)に感謝します。