ニュートン法でx^2-2=0を解くアルゴリズム
- 評価
- クリップ 0
- VIEW 2,872
ニュートン法でx^2-2=0を解くアルゴリズムを作成しております。
下記のコードを実行した場合、1回目の誤差の表示はうまくいくのですが、2回目のWhile文の判定で
エラーが起きます。
引数には2を渡しています。
import java.math.BigDecimal;
public class kinjiti {
public static void main(String args[]) {
BigDecimal x = new BigDecimal(args[0]);
BigDecimal delta = new BigDecimal(1);
BigDecimal sisu = new BigDecimal(1.0E-16);
BigDecimal j = new BigDecimal(2);
int count = 0;
while (sisu.compareTo(delta) < 0) {
x = x.subtract((x.multiply(x).subtract(j)).divide((j.multiply(x))));
delta = x.multiply(x).subtract(j).abs();
++count;
System.out.println(count + "回目 誤差=" + delta);
}
System.out.println("x = " + x);
System.out.println("x * x = " + x.multiply(x));
}
}
【コンソール表示内容(エラー内容)】
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......」と出力される想定でした。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
0
こんにちは
(ほかの方の回答にありますが)BigDecimalではdevideの時計算結果が循環小数の場合,
エラーが出るため,丸める必要があります。(四捨五入や切り上げなど)
私は,四捨五入で計算しましたが
https://java-reference.com/java_number_round.htmlを参考に自分のお好きなものを使って
いろいろ試してみてください.
以下 ソースコードとなります。
import java.math.BigDecimal;
public static void main(String args[]) {
BigDecimal x = new BigDecimal("2.0");
BigDecimal delta = new BigDecimal("1.0");
BigDecimal sisu = new BigDecimal("1.0E-16");
BigDecimal j = new BigDecimal("2.0");
int count = 0;
while (sisu.compareTo(delta) < 0) {
BigDecimal a = (x.multiply(x).subtract(j)).divide(x.multiply(j),BigDecimal.ROUND_HALF_UP);
x = x.subtract(a);
delta = (x.multiply(x).subtract(j)).abs();
count++;
System.out.println(count + "回目 誤差=" + delta);
}
System.out.println("x = " + x);
System.out.println("x * x = " + x.multiply(x));
}
}
(追記)
aの平方根を求めるときのソースとなります。(入力)
import java.util.Scanner;
import java.math.BigDecimal;
public class Sum {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String line = sc.nextLine();
Double n = Double.parseDouble(line);
BigDecimal a = new BigDecimal(n); //√aを求めたい
BigDecimal x = new BigDecimal("2.0");
BigDecimal delta = new BigDecimal("1.0");
BigDecimal sisu = new BigDecimal("1.0E-16");
int count = 0;
while (sisu.compareTo(delta) < 0) {
BigDecimal f = x.multiply(x).subtract(a);
BigDecimal df = x.multiply(new BigDecimal("2.0"));
x = x.subtract(f.divide((df),BigDecimal.ROUND_HALF_UP));
delta = (x.multiply(x).subtract(a)).abs();
count++;
System.out.println(count + "回目 誤差=" + delta);
}
System.out.println("x = " + x);
System.out.println("x * x = " + x.multiply(x));
}
}
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
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にしていることが問題です。
書いてみた
なんとなく無理矢理感の漂うコードではありますが。
import java.math.BigDecimal;
import java.math.RoundingMode;
class Newton {
//
static final BigDecimal epsilon = BigDecimal.valueOf(1.E-16);
static boolean isNearlyZero(BigDecimal num) {
return epsilon.compareTo(num.abs()) > 0;
}
//
final BigDecimal n;
BigDecimal func(BigDecimal x) {
// x^2 - n
return x.pow(2).subtract(n);
}
static BigDecimal devFunc(BigDecimal x) {
// 2x
return BigDecimal.valueOf(2).multiply(x);
}
//
Newton(int n) {
this.n = BigDecimal.valueOf(n);
BigDecimal x = BigDecimal.valueOf(2.);
BigDecimal delta = BigDecimal.valueOf(1.);
for(int count = 1; !isNearlyZero(delta); ++count) {
// x <- x - f(x) / f'(x)
x = x.subtract(
func(x).divide(devFunc(x), RoundingMode.HALF_UP)
);
delta = func(x);
System.out.println(count + "回目 誤差=" + delta);
}
System.out.println("x = " + x);
System.out.println("x * x = " + x.pow(2));
}
public static void main(String[] args) {
try {
new Newton(Integer.parseInt(args[0]));
}
catch(ArrayIndexOutOfBoundsException | NumberFormatException e) {
System.err.println("USAGE: java Newton n");
System.err.println("to compute sqrt(n)");
}
}
}
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.21%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2018/03/23 14:05
回答のとおりに丸めたところ、エラーはなくなりました。ありがとうございます。
当初の質問とは変わってしまい申し訳ないのですが、追加で質問です。
今回は引数に「2」を渡しており、結果も正常に出力されました。
引数に「3」を渡した場合、3の平方根の値がコンソールに出力される想定なのですが、
うまく出力されません。(編集で追記しておきます)
お時間がありましたら、回答いただけますと幸いです。よろしくお願いします。
2018/03/23 15:26 編集
上の回答に追記したのでそちらをご覧ください。
あとソースを書いていく途中 気づいたのですが
値が2である変数jを分子 分母 両方に入れています。
2の平方根を求める場合だけたまたま同じで
一般的には違うので変数を分ける必要があると思います。
改良したソースでは分けています。(正確に言うと2のところは変数にしてません...)
(一般の時(x ^ 2 - a) / (2 * x) で
a = 2の時だけ 分子 分母 両方に2が使われているという意味です。)
2018/03/23 16:10
勘違いをしておりました。2でたまたま通っていただけでしたね…。
追記のソース確認いたしました。想定通りの結果が出ました。
本当にありがとうございました。
2018/03/23 16:41