##はじめに
本日は題名の通り、私がAtcoderの問題を解いていてたまに出てくる、
ある値の平方根が、整数になるかどうか、という判定の為に書いたソースについて
説明していこうと思います。
##失敗例
平方根をどうやって計算しても良いかわからない私は、
とりあえずググって出てきたMath.sqtr(x)を使って平方根を求めることを覚えました。
それをどうやって整数か判断するか、と考えた時、私の頭の中では、
「整数 = 小数点が存在しない数」という式を思いついた為、
そのように書いてみることにしました。
x = gets.split(' ').map(&:to_i)
count = 0
x.times do |n|
a = Math.sqrt(n)
#以下で、小数が存在しないことを判定
if a(-1) == nil
count += 1
end
end
aの-1桁目、小数第一位が存在しない=整数、と言うことを考えていましたが、
見事にこれは不正解となりました。
##何が問題だったのか
上記のソース何が悪かったか、と言いますと、
Math.sqrtを使って、平方根をとった数字は、float型となってしまう点です。
そのため、答えが整数であっても1とはならず、1.0となってしまうので、
小数第一位がどの数字にも、存在することになってしまうのです。
##どうやって解決したか
###失敗した解決策
またしても失敗例となりますが、上記を試してダメだった私は、
「小数が存在しない=小数第一位で切り上げしても数字が変わらない」と考えたため、
切り上げのceilを使って以下のように変更してみました。
x = gets.split(' ').map(&:to_i)
count = 0
x.times do |n|
#以下で、平方根をとった数字に小数が存在しないことを判定
if Math.sqrt(n).ceil == Math.sqrt(n)
count += 1
end
end
ですがこちらも案の定不正解です。
小数第一位が0であっても、x.012395995...となり、
整数とならない可能性を考慮できていませんでした。
###成功した解決策
では、どうやって整数かを判定したのか。
正直私の中では、数字をどう使って判定すれば良いかわかりませんでした。
結果として、最善策でないのは百も承知で、以下のような方法で判定してきました。
x = gets.split(' ').map(&:to_i)
count = 0
x.times do |n|
#以下で、平方根をとった数字に小数が存在しないことを判定
a = Math.sqrt(n).to_s
index = a.index('.')
if a[index+2] == nil
count += 1
end
end
平方根をとった数字を一回文字列に変換します。
そして、'.'が何番目に存在するかを取得し、
'.'の位置から+2番目、つまり小数第2位に当たる数が存在するかどうかを判定し、
存在した場合は、整数でない、存在しない場合は、整数という風に判別しました。
##もうちょっと改善
一応、求めたかった答えを得ることはできましたが、
あまりにもまどろっこしいやり方なため、
もう少しスマートに解きたい、と思っていたところ、
ある方から頂いた助言により、できたのが以下のソースです。
x = gets.split(' ').map(&:to_i)
count = 0
x.times do |n|
#以下で、平方根をとった数字に小数が存在しないことを判定
a = Math.sqrt(n)
if a%1 == 0
count += 1
end
end
平方根をとった数字を1で割って余りが出るかどうか、を判定して、
整数かどうかを見分ける方法ですね。
何を思ったか、私は小数以下は1で割ったら無視されると考えていました。
##最後に
かなり苦しめられた問題でしたが、
自分で不恰好なりにも、一つ斬新?な発想で解を用意し、
最終的には文字列に変えずに、整数を判定する方法がわかったので、
大きな収穫だったと思います。
競技プログラミングをすることで、
コーディングが上達するのかはよくわかりませんが、
与えらた条件をうまく使って、
文字・数値・配列などを自由に扱えるようになれれば、と思います!