はじめに
個人的に好きなアルゴリズム学習サイト「CodeWars」の問題をシェア。
週1くらいのペースで、全10回を目指す予定。
今回は(主観ですが)Rubyの特徴を活かせる問題です。
オススメ問題
問題
- 数字が与えられます
- その数字以下の、3と5の倍数を求めます
- 求めた倍数の合計を返します
※15など、3と5両方の倍数は、一回だけカウントします
※負数の場合はゼロを返します
solution(10) # => 23
# 10未満の、3, 5の倍数の合計
# 3, 5, 6, 9の合計 => 23
難易度
分類は 6kyu です。
アルゴリズム問題として手応えがあるレベル。
ベタ書きもできますが、ぜひきれいなアルゴリズムを目指していきたい。
オススメの回答
「評価が高い回答」の中から、学びの多い回答をピックアップしてご紹介。
模範解答
def solution(n)
sum_up_to = -> i { i * (1..((n - 1) / i)).sum }
sum_up_to.(3) + sum_up_to.(5) - sum_up_to.(15)
end
丁寧に書くと以下の形ですね
def sum_up_to(n, i)
# (n - 1) / i の範囲を作成し、その範囲の合計に i を掛ける
maximum = ((n - 1) / i).floor
(1..maximum).sum * i
end
def solution(n)
# sum_up_toメソッドを使って3、5、15の倍数の合計を計算
sum_up_to(n, 3) + sum_up_to(n, 5) - sum_up_to(n, 15)
end
アルゴリズムを解説します。
与えられた数が10である場合を考えます。
3の倍数は3, 6, 9
5の倍数は5
これはそれぞれ、以下のように変換できます。
3*1, 3*2, 3*3 = 3*(1+2+3)
5*1
よって、x*(1..n)という共通化ができます。
次に、nを求めましょう。
10に対し、3の倍数のnは3です(3 * 3 = 9が最大のため)。
これは10/3 => 3.3333 => 3(切り捨て)で求まります。
ここまでの処理をまとめたものが、メソッドsum_up_toです。
このsum_up_toを使用し、3と5の倍数を求め、2回カウントされている15の倍数を引いて答えを求めます。
短いコードの方では、floorの記述がありません。
これはrangeオブジェクトのstepのデフォルトが1に設定されていることを利用した短縮です。
詳しくはこちら
短いが不足のある解答
def solution(number)
(1...number).select {|i| i%3==0 || i%5==0}.inject(:+)
end
短く読みやすいコードですが、numberが負数であるときに正常に動作しません。
問題文でも「負数の場合はゼロを返す」と示されている以上、負数を無視したコードは不足があると言わざるを得ません。
おわりに
以上、CodeWarsオススメ問題でした。
オススメ問題・解法など、ぜひコメントお待ちしております!