プログラマ脳を鍛える数学パズルQ3:カードを裏返せを解いてみた

プログラマ脳を鍛える数学パズル シンプルで高速なコードが書けるようになる70問(増井 敏克)|翔泳社の本 のQ3を解いてみました。

まずは、ルールを整理してみます。 最初はすべてのカードが裏なので、初期値を-1として、表になればカードナンバーを表示することにします。 f:id:kei_kmj:20220223141327p:plain

この辺で約数に関係するということに気付きます。 f:id:kei_kmj:20220223141610p:plain 素数は1と自分自身の2つしか約数が無いので、自分自身の番で1度だけ表になります。 そこから考えると、 約数が偶数個であれば、最後は表になり、奇数個であれば裏で終わることになります。

なので、解答

# 約数の数が奇数である数字を出力する
def main
  (1..100).each do |num|
    print num, ",\s" if count_in_divisor(num).odd?
  end
end

# 約数の数を計算する
def count_in_divisor(num)
  (1..num).select { |i| (num % i).zero? }.size
end

main

これでもいいのですが、 例えば20という数字の約数を考えるとき、20の1/2の10を超えれば、あとは自分自身しか約数はありません。 そこを考えて実装すれば、計算量が半分程度で済みそうです。

# 自分自身以外の約数が偶数個であれば良い
def main
  (1..100).each do |num|
    print num, ",\s" if count_in_divisor(num).even?
  end
end

# 自分自身以外の約数の数を計算する
def count_in_divisor(num)
  (1..num / 2).select { |i| (num % i).zero? }.size
end

main

ここでもう少し考えてみると、 約数の数が奇数になるときというのは、どんな時でしょう? もう一度20に登場してもらうと、

20 = 1 * 20 = 2 * 10 = 4 * 5

という風に、ふつうは約数はペアになるので、約数の数は偶数です。

約数の数が奇数になるのは

25 = 1 * 25 = 5 * 5 

のように、平方数の時だけです。 ここまでくると、コーディングする必要もなさそうですが、 あえて書くなら

# 約数の数が奇数になるのは平方数の時のみなので、
# 100以下の平方数を出力する
(1..10).map { |n| print "#{n**2}, " }

でしょうか。

答え

$ ruby 03.rb
1, 4, 9, 16, 25, 36, 49, 64, 81, 100,