「第17回オフラインリアルタイムどう書く」参考問題のへなちょこ解答(Java)
久しぶりにイベント(http://atnd.org/events/46348)開催前に参考問題を解けました。
今回は、「回文基数」です!
たとえば101, 234432のように、右から読んでも左から読んでも同じ数字の並びを持つ数を「回文数」というのですね。初めて知りました。
今回は、入力で与えられた数字を変換して、回文数を得ることができる基数を求めなさい、という問題です。
考えたこと
基数は、入力値よりも大きくはなりません。そこで、基数が入力値未満の間、入力値を基数n(n=2から開始し、1ずつ増やす)の値に変換し、回文数かどうかをチェックします。
変換するとはいっても、基数が大きくなってくると、変換後の値を表現するのは大変です。16進数なら16種類の識別子(0〜9とa〜f)があれば値を表現できますが、100進数では100種類の識別子が必要です。
幸い、この問題では「回文になっているか」さえチェックできればよいので、最上位ケタと最下位ケタから中央に向かって1つずつ数字を比較し、変換値のケタの並び順にはこだわらないことにします。
自分の解答
import java.math.BigInteger; import java.util.ArrayList; import java.util.List; /** * Question: http://nabetani.sakura.ne.jp/hena/ord17scheherazade/ */ public class Scheherazade { public String solve(String input) { BigInteger dn = parse(input); StringBuilder sb = new StringBuilder(); for (BigInteger base = BigInteger.valueOf(2); base.compareTo(dn) < 0; base = base .add(BigInteger.ONE)) { List<BigInteger> num = getNum(dn, base); if (0 < num.size() && isPalinum(num)) { sb.append(String.valueOf(base)); sb.append(','); } } if (sb.length() == 0) { return "-"; } return sb.substring(0, sb.length() - 1); } boolean isPalinum(List<BigInteger> num) { for (int i = 0; i < num.size() / 2; i++) { if (0 != num.get(i).compareTo(num.get(num.size() - 1 - i))) { return false; } } return true; } List<BigInteger> getNum(BigInteger origin, BigInteger base) { List<BigInteger> result = new ArrayList<>(); BigInteger answer = origin; for (; 0 < answer.compareTo(BigInteger.ZERO);) { BigInteger[] dr = answer.divideAndRemainder(base); result.add(dr[1]); answer = dr[0]; } return result; } BigInteger parse(String input) { return new BigInteger(input); } }
- 上のコードと、テストコード https://gist.github.com/torazuka/8336832
勉強になった点
intのケタが足りない
問題に用意されたテストデータを使ってテストしたところ、intを使って書いていたせいで、ケタが足りなくて死にました! ksg! BigIntegerさんこんにちは。BigDecimalでもいいですね。
BigIntegerの値の比較はcompareToメソッドを使う
BigIntegerを使った際に、数値の比較でミスをしました。回文数かどうかをチェックするisPalinumメソッドで、変換後の数値を1つの位ごとに格納したListから、2つずつ値を取り出して、等しいかどうか見ています。
ここで、最初にintで書いた名残りで、演算子「!=」を使っていました。これではオブジェクト同士を比較するだけで、値の比較はできません。当然コンパイルは通るので、見逃してしまいました。
BigIntegerのdivideAndRemainderメソッド
配列の1番目の要素で除算の結果、2番目の要素で剰余の結果を返してくれるメソッドです。
剰余を得るならModでいいかと思ったのですが、BigIntegerにはm mod nを返すModメソッドと、m % nを返すRemainderメソッドがあり、実装が異なるんですね。どっちがどうというのは理解していません(宿題?)。
わからなかったのでdivideAndRemainderメソッドを使ったというのが正直なところです。