お世話になっております。
ネットでプログラミング数学というサイトの問題を解いてたときの話なのですが、
**問題** 左右どちらから読んでも同じ値になる数を回文数という. 2桁の数の積で表される回文数のうち, 最大のものは 9009 = 91 × 99 である. では, 3桁の数の積で表される回文数の最大値を求めよ.
という問題があり、プログラミングを使って解いていて、解けはしたのですが、プログラミングを上手く生かしきれず、最終的には出力結果とにらめっこしてマッチする値を探すという手段で答えを出したのですが、プログラミング的にはどうなのかと思い質問しました。
値が6桁の場合、[1][2][3][3][2][1]のような関係があるのは気付いたのですが、プログラミングでの記述方法がわかりませんでした。
↓は私が書いたコードなのですが、プログラミングでこの問題の処理を完結させて、答えを出力させることはできるのでしょうか?
java
1public class hoge { 2 public static void main( String[] args ){ 3 int a = 0; 4 int c = 0; 5 for( int i = 0; i < 90; i++ ){ 6 for( int j = 0; j < 90; j++ ){ 7 a = (999-j)*(999-i); 8 String b = String.valueOf( a ); 9 if( b.matches( "^9.*" ) ){ 10 if( b.matches( ".*9$" ) ){ 11 c++; 12 System.out.println( "【j=" + j + "】【i=" + i + "】" + a ); 13 } 14 }else if( b.matches( "^8.*" ) ){ 15 if( b.matches( ".*8$" ) ){ 16 c++; 17 System.out.println( "【j=" + j + "】【i=" + i + "】" + a ); 18 } 19 } 20 } 21 } 22 System.out.println( "出力結果は" + c + "通りです。" ); 23 } 24} 25
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答8件
0
整数 n が回文数かどうかは以下の関数で判別することができます。
java
1public static boolean isPalindrome(int n) { 2 String string = String.valueOf(n); 3 String reverse = new StringBuilder(string).reverse().toString(); 4 return string.equals(reverse); 5}
これを使ってこんな風に求めることができます。
java
1int max = -1, max1 = -1, max2 = -1; 2for (int i = 100; i <= 999; ++i) { 3 for (int j = i; j <= 999; ++j) { 4 if (isPalindrome(i * j)) { 5 if (i * j > max) { 6 max = i * j; 7 max1 = i; 8 max2 = j; 9 } 10 } 11 } 12} 13System.out.printf("max: %d = (%d * %d)%n", max, max1, max2); 14-> 15max: 906609 = (913 * 993)
投稿2018/05/26 07:54
編集2018/05/26 19:57退会済みユーザー
総合スコア0
0
Problem 4 - Project Euler の問題ですね。
汎用性を持たせるなら任意のN桁について解けるようなロジックを考えるべきでしょう。
最大の数と言うことから、一番大きい数、999 * 999 = 998001
から探していくというのが早いだろうということがわかります。しかし、二つの積の部分、999
と999
の二つを減らしていくとなると、組合せが膨大で、かつ、片方だけを減らしていっても、ちょうどうまい具合のものを見逃してしまいそうです。ですので、減らしていくのは9998001
の方です。
9998001
の減らしていくと言っても回文になるパターンは非常に少ないです。6桁の場合は1000分の1個しか回文になりません。それもそのはずです。単純に前の3桁が決まれば、対応する回文が決定されるからです。
998 -> 998899 997 -> 997799 996 -> 996699 ...
という感じにです。つまり、減らしていくのは前3桁だけが必要です。前3桁さえ決まれば、後3桁も自動的に決まります。そして、重要なことは、前3桁の大小は回文になったときの大小と一致すると言うことです。つまり、998
から減らしていって、条件を満たす数がみつかれば、その数が最大となるというわけです。(減らす限界は100
までである。それ以下の場合は5桁になるため、この方法では回文が得られない。)
こうして、回文候補は大きい方から順に上げることが出来るようになりました。次は、それらが3桁同士の積になるかどうかです。
条件を満たすのであればn = a * b
(n
は回文になる数、a
≧ b
)となる3桁の整数a
とb
が存在することになります。このときa
は3桁なので999
以下です。では逆にあり得る最小はいくつでしょうか?それは√n
です。もし、a
が√n
より小さい場合、b
はa
以下であるため、a * b
は√n * √n = n
より小さくなってしまいます。つまり、もしa
が存在するなら、a
は999
から√n
の間の数となります。
後はわかりますよね。999
から√n
までの整数に対して、回文の数n
が割り切れるかどうかを確認するだけです。もし、割り切れれば、b
が存在し、その数はa
以下であるため、3桁以内になります。(b
が2桁以下であることは確認しなくても良い。最大のパターンでも999 * 99 = 98901
と5桁になるため、6桁を前提しているときには2桁以下になることはない)
桁数を増やしても最大の数が9999
、99999
と増えていくだけ、同じ考え方で解くことが出来ます。
稚拙ですが、参考までにRubyでの実装例です。
Ruby
1def int2list(n) 2 list = [] 3 while (n > 0) 4 n, r = n.divmod(10) 5 list << r 6 end 7 list 8end 9 10def list2int(list) 11 list.each_with_index.map { |n, i| n * (10 ** i) }.sum 12end 13 14def palindrome(n) 15 list = int2list(n) 16 n * (10 ** list.size) + list2int(list.reverse) 17end 18 19def search_palindrome(digit) 20 max_n = 10 ** digit - 1 21 n = max_n ** 2 / 10 ** digit 22 n.downto(10 ** (digit - 1)) do |i| 23 pn = palindrome(i) 24 max_n.downto(Integer.sqrt(pn)) do |j| 25 if pn % j == 0 26 return pn, j, pn / j 27 end 28 end 29 end 30 nil 31end 32 33printf("%d = %d * %d\n", *search_palindrome(2)) 34printf("%d = %d * %d\n", *search_palindrome(3)) 35printf("%d = %d * %d\n", *search_palindrome(4)) 36printf("%d = %d * %d\n", *search_palindrome(5)) 37printf("%d = %d * %d\n", *search_palindrome(6)) 38printf("%d = %d * %d\n", *search_palindrome(7)) 39printf("%d = %d * %d\n", *search_palindrome(8))
計算量は桁数の指数関数オーダー以上のようです(正確なものはよくわからない)。9桁は時間がかかりすぎてRubyでは求められませんでした。C++あたりで書き直せばできるかとも思います。
投稿2018/05/26 07:55
編集2018/05/26 08:11総合スコア21735
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/05/26 09:17
2018/05/26 09:51
退会済みユーザー
2018/05/27 00:19
2018/05/27 00:49
退会済みユーザー
2018/05/27 01:13
0
「3桁の数の積」の最大値は
(1000-1)^2=998001
なので、これ以下で回分数になる数を求めるなり作るなりして、
回分数を素因数分解などして3桁×3桁にできるかチェックしていくというのはどうでしょう?
投稿2018/05/26 05:56
編集2018/05/26 05:58総合スコア20651
0
今更ですが、答えが6桁であることを前提にすると、abccbaと表すことができて、
abccba = 100001 * a + 10010 * b + 1100 * c
ここで100001、10010、1100はすべて11の倍数です。なので6桁の回文数は11の倍数であると分かります。
回文数を生成して3桁の数の積であらわせるかをチェックする戦略と、3桁の数の積を生成して回文数であるかをチェックする戦略が考えられますが、どちらにおいても、この「11の倍数である」であるという性質を利用すれば多少高速化できると思います(計算量のオーダーは変わりませんが)。
投稿2018/05/27 05:10
総合スコア2551
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
回答してくださった皆様、ありがとうございます。
拙いコードですが、一発で結果を出力することができました。
java
1public class hoged { 2 public static void main( String[] args ){ 3 for( int i = 1000; i > 900; i-- ){ 4 for( int j = 1000; j > 900; j-- ){ 5 int a = i * j; 6 String str = String.valueOf( a ); 7 StringBuffer sb = new StringBuffer( str ); 8 String str_2 = sb.reverse().toString(); 9 if( str.matches( str_2 ) ){ 10 System.out.println( str + " = " + i +" x " + j ); 11 return; 12 } 13 } 14 } 15 } 16} 17
投稿2018/05/27 03:23
総合スコア60
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ベストアンサー
まあ 回文数になるプログラム(コピペを避けるために別言語で)
<?php $k = 0; for ( $i = 101; $i < 1000; $i++) { for ( $j = 101; $j < 1000; $j++) { $x = $i * $j; $str = ($x.""); $rev = strrev($x.""); // 文字列反転 if ($str == $rev) { // 反転結果と一致してるかどうか // echo sprintf("%4d", $i); // echo sprintf("%4d", $j); // echo " "; echo $str . PHP_EOL; $k++; // 件数を確認 // ここで最大値判定をする } } } // echo $k . "件" .PHP_EOL;
というように文字列反転して比較してしまえばいいのです。
投稿2018/05/26 07:53
退会済みユーザー
総合スコア0
0
値が6桁の場合、[1][2][3][3][2][1]のような関係があるのは気付いたのですが、プログラミングでの記述方法がわかりませんでした。
この部分ですが、複数の問題に分割すれば簡単に記述可能かと思います。
コードそのものを書くと考える余地が無くなってしまいますので、問題分割の概要のみの回答とさせて頂きます。
- 数値の桁数を求める。
- 各桁の数値を取り出す。
- 回文数かどうか判定する。
まず1.ですが、これは10を底とするlogを計算し、結果を切り上げれば実現可能です。java.lang.Mathクラスにはlog10(double)メソッドとceil(double)メソッドが有りますので、これを使えば簡単かと。
2.に関しては、簡単な数学の問題です。例えば数値aの3桁目の値は「aを100で割って、その値を10で割った余り」です。
3.に関しては、特に解説は不要かと思います。先頭と末尾から順番に判定すれば良いだけですので。
投稿2018/05/26 07:41
総合スコア298
0
3桁の数の積で表される可能性のある回文は
[1][0][0]/[0][0][1]
から
[9][9][9]/[9][9][9]
つまり100-999の組み合わせで記述できます。
ので、先に全回文を作って、それを3桁の数字で割ってみるのがワリと手っ取り早い気がします。3桁の数字のでかい方から割れば、最大値はサクッと見つかるのでは?
追記
php で書いてみました。
上記以外に、ちょっと条件が必要でした^^;
php
1<?php 2function make_palindrome_array(){ 3 $palindrome_array = []; 4 for ($i = 999; $i >= 100; --$i){ 5 $palindrome = (string)$i . strrev((string)$i); 6 $palindrome_array[] = (int)$palindrome; 7 } 8 return $palindrome_array; 9} 10$palindrome_array = make_palindrome_array(); 11foreach($palindrome_array as $palindrome){ 12 for($j = 999; $j >= 100; --$j){ 13 if($palindrome % $j === 0){ 14 if($palindrome / $j <= 999){ 15 echo '最大の回文は'.$palindrome.PHP_EOL; 16 echo '組み合わせは ' . $j . ' と ' . $palindrome / $j . PHP_EOL; 17 break 2; 18 } 19 } 20 } 21}
投稿2018/05/26 07:36
編集2018/05/26 08:06退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2018/05/26 12:38
退会済みユーザー
2018/05/26 19:59
退会済みユーザー
2018/05/27 05:26