■はじめに
Rubyの基礎的な問題をたくさん解くことで基本的な考え方やメソッドの使い方を定着させたい。 基本的にはAtCoderというプログラミングコンテスト(競技プログラミング)の過去問を使う。(AtCoderは難易度が分かれており、最も簡単なA問題を解いていく)
■問題
●出典
AtCoder Beginner Contest 009のA問題 https://atcoder.jp/contests/abc009/tasks/abc009_1
●問題文
高橋君は引越し作業の真っ最中です。荷物をすべてダンボールにまとめ終え、ちょうどいま家の前にトラックが到着したところです。高橋君は N 個あるダンボールをすべてトラックまで運ぶ必要があります。
ダンボールはとても重いですが、高橋君は力持ちなので片手で 1 つのダンボールを持つことができます。つまり、家とトラックの間を 1 往復するときに最大でそれぞれの手にダンボールを 1 個ずつ、合計で 2 個のダンボールをいちどに持って運ぶことができます。
ダンボールの個数が多いため、高橋君は N 個すべてのダンボールを運びきるために家とトラックの間を最低何往復しなければならないのかが気になりました。
●入力
入力は以下の形式で標準入力から与えられる。
N
1 行目には、ダンボールの個数を表す整数 N (1≦N≦1,000) が与えられる。
●出力
ダンボールをすべて運びきるために必要な最低の往復回数を 1 行に出力せよ。
■回答
●愚直に書く
シンプルに、2で割って繰り上げ、で大丈夫かな? 整数同士で割り算してしまうと小数点を切り捨てられてしまうのでto_fを使う。
n = gets.to_f
puts (n / 2).ceil
通った!
●リファクタリング/別アプローチ
「奇数なら1を足してから2で割る」でもできそうな気がするのでやってみる。
n = gets.to_i
if n.odd?
puts (n + 1) / 2
else
puts n / 2
end
三項演算子も使うとこうなる。
n = gets.to_i
puts n.odd? ? ((n + 1) / 2) : (n / 2)
通った!ただ、パッと見何をしているのかわかりにくい気もする。
●他の方の回答例
この回答が面白かった。(上位で結構この回答多かった)
p (gets.to_i+1)/2
to_1して1を足して2で割る。これでなぜ行けるんだろうかと思ったけど、
- 偶数 → 1を足してから2で割っても0.5は切り捨てられるからOK
- 奇数 → 1を足すことで切り捨てられてしまう部分が1くり上がって正しい結果になる
ということで結果は正しくなる。なるほどな〜!僕の別案は奇数を判定してたけど、判定する必要がないってことか。もっというとわざわざ判定するのは無駄ということが。これもコードを見て何をしているのかを掴みにい気はするけど、すごく面白い。
●出てきたメソッド
公式リファレンスを見る訓練。
-
to_f https://docs.ruby-lang.org/ja/latest/method/String/i/to_f.html
f
はFloat(浮動小数点数)のf
-
ceil https://docs.ruby-lang.org/ja/latest/method/Float/i/ceil.html
- 引数に正の整数を指定するとその桁分まで繰り上げたFloatを返す
- 引数に負の整数を指定すると小数点の左側にn個分の0が並んだIntegerを返す
1.234567.ceil(2) # => 1.24
1.234567.ceil(3) # => 1.235
1.234567.ceil(4) # => 1.2346
1.234567.ceil(5) # => 1.23457
34567.89.ceil(-5) # => 100000
34567.89.ceil(-4) # => 40000
34567.89.ceil(-3) # => 35000
- odd?
https://docs.ruby-lang.org/ja/latest/method/Integer/i/odd=3f.html
- ちなみに偶数かどうかの判定は
even?
https://docs.ruby-lang.org/ja/latest/method/Integer/i/even=3f.html
- ちなみに偶数かどうかの判定は
■振り返りなど
- 色々な解法を確認できて面白かった。
- 三項演算子はわかりにくくなることもあるので気をつけて使おうと思った。
ceil
で負の整数を引数にする方法を知らなかったので勉強になった。出てきたメソッドのるりまを眺めるというのは良さそう。