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

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

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

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

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Q&A

解決済

2回答

4221閲覧

ニュートン法でx^2-2=0を解くアルゴリズム

Watanabe_ef

総合スコア28

Java

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

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

0グッド

0クリップ

投稿2018/03/22 07:07

編集2018/03/23 05:09

ニュートン法でx^2-2=0を解くアルゴリズムを作成しております。

下記のコードを実行した場合、1回目の誤差の表示はうまくいくのですが、2回目のWhile文の判定で
エラーが起きます。

引数には2を渡しています。

Java

1import java.math.BigDecimal; 2 3public class kinjiti { 4 public static void main(String args[]) { 5 BigDecimal x = new BigDecimal(args[0]); 6 BigDecimal delta = new BigDecimal(1); 7 BigDecimal sisu = new BigDecimal(1.0E-16); 8 BigDecimal j = new BigDecimal(2); 9 int count = 0; 10 while (sisu.compareTo(delta) < 0) { 11 x = x.subtract((x.multiply(x).subtract(j)).divide((j.multiply(x)))); 12 delta = x.multiply(x).subtract(j).abs(); 13 ++count; 14 System.out.println(count + "回目 誤差=" + delta); 15 } 16 System.out.println("x = " + x); 17 System.out.println("x * x = " + x.multiply(x)); 18 } 19} 20

【コンソール表示内容(エラー内容)】
1回目 誤差=0.25
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.base/java.math.BigDecimal.divide(BigDecimal.java:1716)
at Newton1.main(Newton1.java:12)

どうして1回目は計算できているのに2回目でエラーが起きるのかわかりません。

ご教示お願いいたします。

【追記】
上記のとおりプログラムを修正したところ、2の平方根が正しく出力されました。

【コンソール】
1回目 誤差=0.2500
2回目 誤差=0.00703889
3回目 誤差=0.0000061592637476
4回目 誤差=4.74200288533215216656E-12
5回目 誤差=2.8108239331373466350562834003132792751609E-24
6回目 誤差=9.8759139788721286232385069031337583760272284804383992169099233938623991426116900E-49
7回目 誤差=1.219170961476023987580884253005982643084857060464517172599001310224667466278282886591601613436503357212794231959984165464850947330556127739804351086766287896576E-97
x = 1.41421356237309504880168872420969807856967187537694807317667973799073247846210703885038753432764161583921656050189505811755022976
x * x = 2.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001219170961476023987580884253005982643084857060464517172599001310224667466278282886591601613436503357212794231959984165464850947330556127739804351086766287896576

しかし、ソースの[x][j]に引数3.0を指定すると、誤差の表示がループします。
コンソールに誤差が7回ほど出力され、「x = 1.3720508......」と出力される想定でした。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

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

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

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

guest

回答2

0

ベストアンサー

こんにちは
(ほかの方の回答にありますが)BigDecimalではdevideの時計算結果が循環小数の場合,
エラーが出るため,丸める必要があります。(四捨五入や切り上げなど)
私は,四捨五入で計算しましたが
https://java-reference.com/java_number_round.htmlを参考に自分のお好きなものを使って
いろいろ試してみてください.
以下 ソースコードとなります。

Java

1import java.math.BigDecimal; 2 3public static void main(String args[]) { 4 5 BigDecimal x = new BigDecimal("2.0"); 6 BigDecimal delta = new BigDecimal("1.0"); 7 BigDecimal sisu = new BigDecimal("1.0E-16"); 8 BigDecimal j = new BigDecimal("2.0"); 9 int count = 0; 10 while (sisu.compareTo(delta) < 0) { 11 BigDecimal a = (x.multiply(x).subtract(j)).divide(x.multiply(j),BigDecimal.ROUND_HALF_UP); 12 x = x.subtract(a); 13 delta = (x.multiply(x).subtract(j)).abs(); 14 count++; 15 System.out.println(count + "回目 誤差=" + delta); 16 } 17 System.out.println("x = " + x); 18 System.out.println("x * x = " + x.multiply(x)); 19 } 20}

(追記)
aの平方根を求めるときのソースとなります。(入力)

Java

1import java.util.Scanner; 2import java.math.BigDecimal; 3 4public class Sum { 5 6 public static void main(String[] args) { 7 8 Scanner sc = new Scanner(System.in); 9 String line = sc.nextLine(); 10 Double n = Double.parseDouble(line); 11 BigDecimal a = new BigDecimal(n); //√aを求めたい 12 BigDecimal x = new BigDecimal("2.0"); 13 BigDecimal delta = new BigDecimal("1.0"); 14 BigDecimal sisu = new BigDecimal("1.0E-16"); 15 16 int count = 0; 17 while (sisu.compareTo(delta) < 0) { 18 BigDecimal f = x.multiply(x).subtract(a); 19 BigDecimal df = x.multiply(new BigDecimal("2.0")); 20 x = x.subtract(f.divide((df),BigDecimal.ROUND_HALF_UP)); 21 delta = (x.multiply(x).subtract(a)).abs(); 22 count++; 23 System.out.println(count + "回目 誤差=" + delta); 24 } 25 System.out.println("x = " + x); 26 System.out.println("x * x = " + x.multiply(x)); 27 } 28 29 30}

投稿2018/03/22 07:56

編集2018/03/23 06:29
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

Watanabe_ef

2018/03/23 05:05

回答ありがとうございます。 回答のとおりに丸めたところ、エラーはなくなりました。ありがとうございます。 当初の質問とは変わってしまい申し訳ないのですが、追加で質問です。 今回は引数に「2」を渡しており、結果も正常に出力されました。 引数に「3」を渡した場合、3の平方根の値がコンソールに出力される想定なのですが、 うまく出力されません。(編集で追記しておきます) お時間がありましたら、回答いただけますと幸いです。よろしくお願いします。
退会済みユーザー

退会済みユーザー

2018/03/23 06:30 編集

aの平方根の値を求めるためにaを入力する必要があります 上の回答に追記したのでそちらをご覧ください。 あとソースを書いていく途中 気づいたのですが 値が2である変数jを分子 分母 両方に入れています。 2の平方根を求める場合だけたまたま同じで 一般的には違うので変数を分ける必要があると思います。 改良したソースでは分けています。(正確に言うと2のところは変数にしてません...) (一般の時(x ^ 2 - a) / (2 * x) で a = 2の時だけ 分子 分母 両方に2が使われているという意味です。)
Watanabe_ef

2018/03/23 07:10

回答ありがとうございます。 勘違いをしておりました。2でたまたま通っていただけでしたね…。 追記のソース確認いたしました。想定通りの結果が出ました。 本当にありがとうございました。
退会済みユーザー

退会済みユーザー

2018/03/23 07:41

私もニュートン法について深めることができてよかったです!!
guest

0

これですかね。BigDecimal.divide() 使用時の注意

BigDecimalクラスの割り算を行うdivide()メソッドにて、計算結果が循環小数になる場合

java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
が発生するので注意が必要。

ただし、丸め方はRoundingModeを用いて指定した方が良いかと。
Qiita -【Java】BigDecimalをちゃんと使う2018

また、定数はfinal指定した方が読みやすいですよ。

コメントを受けて

しかし、ソースの[x][j]に引数3.0を指定すると、誤差の表示がループします。

x = x.subtract((x.multiply(x).subtract(j)).divide((j.multiply(x))));
『指数の2』と『定数項の-2』では絶対値は同じでも役割が全く異なります。
数値が同じだからと言って、双方を同じjにしていることが問題です。

書いてみた

なんとなく無理矢理感の漂うコードではありますが。

Java

1import java.math.BigDecimal; 2import java.math.RoundingMode; 3 4class Newton { 5 // 6 static final BigDecimal epsilon = BigDecimal.valueOf(1.E-16); 7 static boolean isNearlyZero(BigDecimal num) { 8 return epsilon.compareTo(num.abs()) > 0; 9 } 10 11 // 12 final BigDecimal n; 13 BigDecimal func(BigDecimal x) { 14 // x^2 - n 15 return x.pow(2).subtract(n); 16 } 17 static BigDecimal devFunc(BigDecimal x) { 18 // 2x 19 return BigDecimal.valueOf(2).multiply(x); 20 } 21 22 // 23 Newton(int n) { 24 this.n = BigDecimal.valueOf(n); 25 26 BigDecimal x = BigDecimal.valueOf(2.); 27 BigDecimal delta = BigDecimal.valueOf(1.); 28 29 for(int count = 1; !isNearlyZero(delta); ++count) { 30 // x <- x - f(x) / f'(x) 31 x = x.subtract( 32 func(x).divide(devFunc(x), RoundingMode.HALF_UP) 33 ); 34 delta = func(x); 35 36 System.out.println(count + "回目 誤差=" + delta); 37 } 38 39 System.out.println("x = " + x); 40 System.out.println("x * x = " + x.pow(2)); 41 } 42 43 public static void main(String[] args) { 44 try { 45 new Newton(Integer.parseInt(args[0])); 46 } 47 catch(ArrayIndexOutOfBoundsException | NumberFormatException e) { 48 System.err.println("USAGE: java Newton n"); 49 System.err.println("to compute sqrt(n)"); 50 } 51 } 52}

投稿2018/03/22 07:28

編集2018/03/23 06:17
LouiS0616

総合スコア35660

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

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

Watanabe_ef

2018/03/23 05:11

回答ありがとうございました。 数値を丸めることで、エラーは解消されました。 追記がありますので、お時間が許せばそちらの方にも回答していただけると幸いです。
LouiS0616

2018/03/23 05:32 編集

そのまま適用したら導関数がプログラム上で3xになりませんか? 変数jをどこで用いるべきか再考した方が良いかと。
Watanabe_ef

2018/03/23 07:09

回答ありがとうございました。 大変な勘違いをしていました。変数を2で実行していたからたまたま通っていたんですね・・・。 参考にさせていただきます!ありがとうございました!
退会済みユーザー

退会済みユーザー

2018/03/23 07:40 編集

私もニュートン法について深めることができてよかったです!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問