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

2010 年 6 月 14 日 by 山平

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

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

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

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

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

普通の倍数判定

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

RUBY:
  1. def by3d(n)
  2. (n % 3 == 0)
  3. end
  4.  
  5. def by7d(n)
  6. (n % 7 == 0)
  7. end
  8.  
  9. def by11d(n)
  10. (n % 11 == 0)
  11. end

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

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

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

RUBY:
  1. def by3p(n)
  2. while n.to_s.length> 1 do
  3. n = eval(n.to_s.split(//).join('+'))
  4. end
  5. (n % 3 == 0)
  6. end

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

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

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

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

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

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

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

RUBY:
  1. def by11p(n)
  2. d = "-+".split(//)
  3. while n> 9 do
  4. c = n.to_s.split(//)
  5. candi = ''
  6. candi = candi <<d[c.length%2] <<c.shift while c.length> 0
  7. n = eval(candi).abs
  8. end
  9. (n == 0)
  10. end

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

検算を自動化する

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

RUBY:
  1. def checkresult(maxcount, methods)
  2. ok, ng = 0, 0
  3. maxcount.times do |t|
  4. r = []
  5. methods.each do |m|
  6. r.push(method(m).call(t))
  7. end
  8. result = (r.uniq.length == 1)
  9. ok += 1 if result
  10. ng += 1 if !result
  11. puts "#{t}: #{result} ok:#{ok}, ng:#{ng}\n" if !result# debug
  12. end
  13. (ng == 0)
  14. end

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

RUBY:
  1. maxcount = 100000
  2. checkresult(maxcount, [:by3d,  :by3p ])
  3. checkresult(maxcount, [:by7d,  :by7p ])
  4. checkresult(maxcount, [:by11d, :by11p])

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

TrackBack