質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

8回答

2787閲覧

3桁の数の積で表される回文数の最大値を求める処理を、プログラミングで完結させたい。

Nerd_run.

総合スコア60

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2018/05/26 05:23

編集2018/05/26 05:24

お世話になっております。
ネットでプログラミング数学というサイトの問題を解いてたときの話なのですが、

**問題** 左右どちらから読んでも同じ値になる数を回文数という. 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ページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/05/26 12:38

結果に影響はないですが、3桁どうしの掛け算なので i < 999, j < 999 のところは i <= 999, j <= 999 または i < 1000, j < 1000にすべきだと思います。
退会済みユーザー

退会済みユーザー

2018/05/26 19:59

@Stars1024 さん ありがとうございます。
退会済みユーザー

退会済みユーザー

2018/05/27 05:26

@game-overさんへ ソースコードを変更して頂き、ありがとうございました。
guest

0

Problem 4 - Project Euler の問題ですね。

汎用性を持たせるなら任意のN桁について解けるようなロジックを考えるべきでしょう。

最大の数と言うことから、一番大きい数、999 * 999 = 998001から探していくというのが早いだろうということがわかります。しかし、二つの積の部分、999999の二つを減らしていくとなると、組合せが膨大で、かつ、片方だけを減らしていっても、ちょうどうまい具合のものを見逃してしまいそうです。ですので、減らしていくのは9998001の方です。

9998001の減らしていくと言っても回文になるパターンは非常に少ないです。6桁の場合は1000分の1個しか回文になりません。それもそのはずです。単純に前の3桁が決まれば、対応する回文が決定されるからです。

998 -> 998899 997 -> 997799 996 -> 996699 ...

という感じにです。つまり、減らしていくのは前3桁だけが必要です。前3桁さえ決まれば、後3桁も自動的に決まります。そして、重要なことは、前3桁の大小は回文になったときの大小と一致すると言うことです。つまり、998から減らしていって、条件を満たす数がみつかれば、その数が最大となるというわけです。(減らす限界は100までである。それ以下の場合は5桁になるため、この方法では回文が得られない。)

こうして、回文候補は大きい方から順に上げることが出来るようになりました。次は、それらが3桁同士の積になるかどうかです。

条件を満たすのであればn = a * b(nは回文になる数、ab)となる3桁の整数abが存在することになります。このときaは3桁なので999以下です。では逆にあり得る最小はいくつでしょうか?それは√nです。もし、a√nより小さい場合、ba以下であるため、a * b√n * √n = nより小さくなってしまいます。つまり、もしaが存在するなら、a999から√nの間の数となります。

後はわかりますよね。999から√nまでの整数に対して、回文の数nが割り切れるかどうかを確認するだけです。もし、割り切れれば、bが存在し、その数はa以下であるため、3桁以内になります。(bが2桁以下であることは確認しなくても良い。最大のパターンでも999 * 99 = 98901と5桁になるため、6桁を前提しているときには2桁以下になることはない)

桁数を増やしても最大の数が999999999と増えていくだけ、同じ考え方で解くことが出来ます。

稚拙ですが、参考までに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
raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Nerd_run.

2018/05/26 09:15 編集

回答ありがとうございます。 そうです、そのサイトです。 英語が読めないので和訳版ですが。 応用は聞かないのを書き直して見たらめちゃくちゃ処理が重く、pc的に無理そうなのができました(´o`; public class hoged { public static void main( String[] args ){ int a = 999 * 999 / 1000; for( int i = 0; i < 10; ){ for( int j = 0; j < 10; j++ ){ int b = ( 999 - i ) * ( 999 - j ); String aa = String.valueOf( b ); for( int l = 0; l < 100; l++ ){ int c = a - l; String bb = String.valueOf( c ); if( aa.matches( "^bb.*" ) ){ System.out.println( bb ); } } } } } }
raccy

2018/05/26 09:17

二つの積の両方をループで回せば最大値に対する2乗のオーダーの計算量になるので遅くなりますよ。いかに片方だけをいくつまで回せば確認が完了できるか考えるのかが重要です。
Nerd_run.

2018/05/26 09:51

あ、重いのは無限ループでした。 んー、難しい。
退会済みユーザー

退会済みユーザー

2018/05/27 00:19

> いかに片方だけをいくつまで回せば確認が完了できるか考えるのかが重要です。 ん?最も重要なのは、いかに回文を作り出すかでは?
raccy

2018/05/27 00:49

> ん?最も重要なのは、いかに回文を作り出すかでは? え?引っかかる所ってそっちなの?私が競プロに毒されているだけ?
退会済みユーザー

退会済みユーザー

2018/05/27 01:13

> 重要なことは、前3桁の大小は回文になったときの大小と一致すると言うことです。 回文が作れることに気がつくことと、上記が最重要かなぁと思ったので。 でも言い換えると、`いかに片方だけを回せば`ってことになるから、あんまり意味のない指摘でしたね。失礼^^;
guest

0

「3桁の数の積」の最大値は
(1000-1)^2=998001
なので、これ以下で回分数になる数を求めるなり作るなりして、
回分数を素因数分解などして3桁×3桁にできるかチェックしていくというのはどうでしょう?

投稿2018/05/26 05:56

編集2018/05/26 05:58
swordone

総合スコア20651

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Nerd_run.

2018/05/26 07:22

回答ありがとうございます。 回文数の部分はプログラムで求めることはできないのでしょうか?
swordone

2018/05/27 00:07

[1][2][3][3][2][1]のようになっているということは、 (100000+1)*[1]+(10000+10)*[2]+(1000+100)*[3]のような計算で求められるということになります。この考えで順次ループさせて作ることができます。
guest

0

今更ですが、答えが6桁であることを前提にすると、abccbaと表すことができて、
abccba = 100001 * a + 10010 * b + 1100 * c

ここで100001、10010、1100はすべて11の倍数です。なので6桁の回文数は11の倍数であると分かります。

回文数を生成して3桁の数の積であらわせるかをチェックする戦略と、3桁の数の積を生成して回文数であるかをチェックする戦略が考えられますが、どちらにおいても、この「11の倍数である」であるという性質を利用すれば多少高速化できると思います(計算量のオーダーは変わりませんが)。

投稿2018/05/27 05:10

karamarimo

総合スコア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

Nerd_run.

総合スコア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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Nerd_run.

2018/05/27 03:27

回答ありがとうございます。 一番シンプルでわかりやすかったです。 件数は出力とにらめっこできるレベルか見るために表示してたものなんですが、 丁寧にありがとうございますw
guest

0

値が6桁の場合、[1][2][3][3][2][1]のような関係があるのは気付いたのですが、プログラミングでの記述方法がわかりませんでした。

この部分ですが、複数の問題に分割すれば簡単に記述可能かと思います。
コードそのものを書くと考える余地が無くなってしまいますので、問題分割の概要のみの回答とさせて頂きます。

  1. 数値の桁数を求める。
  2. 各桁の数値を取り出す。
  3. 回文数かどうか判定する。

まず1.ですが、これは10を底とするlogを計算し、結果を切り上げれば実現可能です。java.lang.Mathクラスにはlog10(double)メソッドとceil(double)メソッドが有りますので、これを使えば簡単かと。

2.に関しては、簡単な数学の問題です。例えば数値aの3桁目の値は「aを100で割って、その値を10で割った余り」です。

3.に関しては、特に解説は不要かと思います。先頭と末尾から順番に判定すれば良いだけですので。

投稿2018/05/26 07:41

rtr1950x

総合スコア298

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rtr1950x

2018/05/26 08:02

一個一個の問題は比較的簡単なので、まずは手計算で概要を掴んで、それから実装してみてはどうでしょうか? 他の方の回答にある様に文字列に変換して比較するのが「楽」ではありますが。 ただ、今後のスキルアップを目標にするなら、頑張って実装してみてはどうかと思います。
Nerd_run.

2018/05/26 08:23

ありがとうございます。 他の方もわかりやすく書いてくださっているので参考にうまくいくまでやって見ます。
guest

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

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問