CodeIQ:割り算

私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「割り算」(CodeIQ)を参照してください。

問題の概要

問題を引用します。
【概要】
割り算をしてください。

【入力】
入力は
1230/10
のようになっています。
除算記号は「/」です。
除数・被除数ともに、正の整数です。

【出力】
出力は、下表のルールに従ってください。

除算の結果形式
整数の場合 整数として出力してください。
小数点や「/1」などをつけてはいけません。
123
有限小数で表現できる場合 有限の小数として出力してください。
先頭は、値が1未満の場合は「0.」で、それ以外は 0 以外にしてください。
末尾は 0 以外にしてください。
123.45
上記以外の場合 既約分数として出力してください。
右の例の通り「/」の前後に空白は不要です。
41/15

先ほどの入力に対応する出力は
123
になります。

【例】
入力 出力
1230/10 123
12345/100 123.45
123/45 41/15
2/5 0.4

【補足】
不正な入力に対処する必要はありません。
除数・被除数 ともに 一兆 以下です。
入力は必ず「被除数/除数」の形式になっています。
余計な空白はありませんし、「100/10/3」のような割り算記号が複数現れる問題もありません。
1より大きな有理数を分数形式で出力する場合、帯分数ではなく仮分数で出力してください。

私のプログラム

Rubyで解答しています。

#!/usr/bin/ruby
require 'prime'

def isRecurring(r)
	divs = r.denominator.prime_division
	for d in divs
		return true if (d[0] != 2) && (d[0] != 5)
	end
	return false
end

def solve(m, n)
	r = Rational(m,n)

	return r.to_i.to_s if r.denominator == 1
	return r.to_s if isRecurring(r)
	return r.to_f.to_s
end

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

	m,n = line.split('/').map{|a| a.to_i}
	puts solve(m, n)
end

解説

RubyにはRationalがあるので簡単です。
有理数を扱う手段のない言語だと面倒だと思います。

考え方

RubyにはRational型があるので約分のことは考えなくても大丈夫です。
なのでポイントは整数になるか、有限小数になるか、それ以外かの判定方法になります。
整数は簡単で分子/分母が割り切れれば良いだけです。
有限小数は入力値を既約にした時、分母を素因数分解した時2と5だけで構成されているなら有限小数になります。

なのでこの判定を行って結果を出力すれば良いことになります。

main

入力値を分子と分母に分けてsolve()に渡し、結果を印字します。

solve()

まず、m/nをRationalにします。
Rubyは便利でこの時点で既約になります。

分母が1の時は整数なので分子を返却します。
isRecurring()は有限小数かどうかを判定する関数で、これがtrueならrを文字列にしたもの、つまり既約のm/nの文字列を返します。
それ以外はrを浮動小数点にして返します。

isRecurring(r)

考え方に書いた通りですが、分母を素因数分解し、その中に2か5以外のものがあればfalseを返し、そうでなければtrueを返します。

雑感

Rubyでやったので簡単でした。
問題を解いていた時に小数で返す場合に単に浮動小数点にして大丈夫か、というのが気になりました。つまり、2/5のようなな場合に20/5=4と整数で計算して、結果の文字列を0.4のようにするような必要があるかということが気になったわけです。
面倒だったのでダメだったら対応しようと思って実行したらOKだったので単純に浮動小数点にすればよかったのですが、これは備考に書いておいて欲しかった。