CodeIQ:極めよプログラミング道!【実力判定:Aランク】

私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「極めよプログラミング道!【実力判定:Aランク】」(CodeIQ)を参照してください。

問題の概要

問題を引用します。
【問題】
1から1,000,000までの整数の範囲で、連続する2値を合計します。
2値の合計が、整数Nの倍数になる組み合わせを、数えてください。
ただし、整数Nは、2≦N≦1,000 の範囲とします。


整数Nが11の場合、「5, 6(合計は11)」、「16,17(合計は33)」……という組み合わせが、整数Nの倍数になります。
また、連続する2値の最小は「1, 2(合計は3)」、最大は、「999999, 1000000(合計は1999999)」になります。

【入力】
標準入力から、複数行のデータが与えられます。1行のデータが、1つの整数Nになります。

【出力】
1行ずつ処理を行ない、その答えを1行ごと標準出力に出力します。

【入出力サンプル】
Input
307
456
545
165

Output
3257
0
1835
6061

私のプログラム

Rubyで解答しています。

#!/usr/bin/ruby

def solve(n)
	if n%2 == 0 then return 0 end

	cnt = 0
	i = 1
	while i*n <= 1999999
		cnt += 1
		i += 2
	end

	return cnt
end

# main
while line = gets
	line.strip!
	if line.empty? then next end

	p solve(line.to_i)
end

解説

私がCodeIQの問題をやり始めてから2回目の実力判定問題です。
Aランクで☆☆☆ですが定期的に出題している出題者の問題の☆☆☆に比べるとはるかに簡単です。また、プログラミング能力よりも数学能力が要求される問題です。

考え方

きちんと数学的に解けば計算だけで求められます(採点結果に解説がありました)。
私はそこまでせずに数えています。

まず、連続した2数の和になるということは奇数です。
なので入力値が偶数なら答えは0です。
入力値が奇数の場合、x倍しても連続した2数の和になる=奇数なのでxは奇数であることがわかります。
なのでN*x(xは奇数)が1999999以下の範囲でxが何個あるかを数えれば良いわけです。

main

solve()関数に渡した結果を印字します。

solve(n)

考え方に書いた通りの実装です。
cntが個数、iが奇数のカウンタです。
別に連続した2数の和になることを確認する必要はありません。

雑感

テストケースの入力数が1000個もあったのでちょっと驚きました。問題文に書かれていたら真面目に数学的に解こうとしたと思います。実際にはこのコードでも1000個を0.2秒で処理できていましたが。

ちなみに計算で求められそうだというのは気づいていました。
1999999/Nの半分(ただしNは奇数)が大体答えになります。考えれば当たり前で連続した2数の和の最大が1999999なのでN*xはそれ以下で最も大きくなるようにxを取れば良いわけです。xのうち約半分が奇数なので1999999/Nの半分が答えになるというのは正しいでしょう。
これはすぐに気づいたのですが端数の扱いをどうすべきか考えるのが面倒になってしまい、回答のコードのように何も考えずに数えてしまいました。