さんすうRuby – 3と7と11の倍数

2010 年 6 月 14 日 by 山平

事の発端は先日見かけたサイトでした。

No.055 「111」は「3」の倍数

1 + 1 + 1 = 3なので、111は3の倍数なんだそうです。

えっ、そうなの?と思って読んでみると、証明まで載っています。
ナルホドォ~興味深い。。。
興味深いついでに、プログラムで確認してみたくなったので、早速Rubyで実装してみます。

倍数の判定方法が興味深く、素数でもある3と7と11について試してみることにします。

普通の倍数判定

まずは普通に割って余りが0かどうかを確認するプログラムを準備します。
これは検算に使います。

def by3d(n)
(n % 3 == 0)
end

def by7d(n)
(n % 7 == 0)
end

def by11d(n)
(n % 11 == 0)
end

要は値の剰余が0なら割り切れると判断しています。

3の倍数かどうかを判別する

3の倍数かどうかを調べるには各桁の数字を合計した数が3の倍数かどうかを見ればよいとのこと。
各桁の数の合計が2桁以上のときの値が3の倍数かどうかを調べるには、各桁の…と1桁になるまで繰り返してよいことが分かります。

def by3p(n)
while n.to_s.length > 1 do
n = eval(n.to_s.split(//).join('+'))
end
(n % 3 == 0)
end

数値を文字列化(to_s)して分割(split(//))、各文字を”+”ではさんで再文字列化(join(‘+’))して計算(eval)します。
これを文字列が1桁になるまで繰り返しています。

7の倍数かどうかを判別する

7の倍数かを判定する方法は、いくつかありますが、なるべく桁数を気にしたくないので、2桁目以上の値から1桁目を2倍した値を引いていく方法を採用しました。

def by7p(n)
while n > 9 do
n = eval(n.to_s[0..-2] + " - 2 * " + n.to_s[-1])
end
(n % 7 == 0)
end

3の倍数と同じように文字列化(to_s)した後、2桁目以上の値(n.to_s[0..-2])から1桁目を2倍した値を引く(” – 2 * ” + n.to_s[-1])式を作成して計算(eval)しています。
ここで注意しなければいけないのが、文字列の場合、2桁の正数と1桁の負数が判別できないので、10以上の間繰り返すようにしています。

11の倍数かどうかを判別する

11の倍数は奇数桁の合計と偶数桁の合計の差が11の倍数かどうかで判別します。
要は各桁を交互に+-していけば良いことが分かります。

def by11p(n)
d = "-+".split(//)
while n > 9 do
c = n.to_s.split(//)
candi = ''
candi = candi << d[c.length%2] << c.shift while c.length > 0
n = eval(candi).abs
end
(n == 0)
end

3の倍数の時のように、単純にsplit(//).join(‘+’)で式が作成できないので、交互に+-を取得(d[c.length%2])しています。
1桁になるまでに負の値になることがあるので、ここでは結果の絶対値(eval(candi).abs)を次に渡すようにしています。

検算を自動化する

検算は最初に作ったメソッドとの結果を比較します。

def checkresult(maxcount, methods)
ok, ng = 0, 0
maxcount.times do |t|
r = []
methods.each do |m|
r.push(method(m).call(t))
end
result = (r.uniq.length == 1)
ok += 1 if result
ng += 1 if !result
puts "#{t}: #{result} ok:#{ok}, ng:#{ng}\n" if !result# debug
end
(ng == 0)
end

メソッドをcallしている箇所がなんだか不細工な気がしますが、それぞれの倍数判別ロジックを好きな回数検算できます。

maxcount = 100000
checkresult(maxcount, [:by3d,  :by3p ])
checkresult(maxcount, [:by7d,  :by7p ])
checkresult(maxcount, [:by11d, :by11p])

駆け足になりましたが以上です。

TrackBack