平方根の計算方法とは?

このQ&Aのポイント
  • 平方根の計算方法にはニュートン法があります。
  • ニュートン法はバビロニア人が使用していた方法で、二次関数の微分を利用しています。
  • Javaで平方根を計算する際にはBigDecimalクラスを使用することが一般的です。
回答を見る
  • ベストアンサー

平方根 ニュートン法について

こんにちは。趣味でプログラミングをしているものです。 さっそくですが、質問させていただきます。 Javaで文具店などで1000円くらいで市販されている 電卓を再現しようとしているのですが、 質問させていただきたいのは、 平方根の計算についてです。 -------------------------------------------- まずは以下のメソッドのコードを見ていただきたいのですが・・・ このメソッドを用いて3の平方根を求めてみました。 private static BigDecimal sqrt( BigDecimal value ) { BigDecimal two = new BigDecimal( "2" ); BigDecimal x = value.divide( two ); BigDecimal last_x = BigDecimal.ZERO; BigDecimal gap = x.subtract( last_x ); BigDecimal range = BigDecimal.ONE.movePointLeft( 11 );//(a) //int cnt = 0;//(b) BigDecimal t; while( gap.compareTo(range) > 0 )//(c) //while( cnt < 20 )//(d) { last_x = new BigDecimal( x.toString() ); t = value.divide( x, 64, BigDecimal.ROUND_DOWN ); x = x.add( t ).divide( two ); gap = x.subtract( last_x );//(e) //cnt ++; //(f) } return x; } (a)(c)(e) を用いて実行すると 1.732142857142857142857・・・ と小数部分の142857 が循環する値になりました。 (a)(c)(e) をコメントアウトして (b)(d)(f) を用いて実行すると 1.732050807568877293527446341505872366942805253 810380628055806979425806427001953125 とwikipedia の「3の平方根」の記事の値と 記述されている範囲では同じ値が得られました。 -------------------------------------------- そこで質問なのですが、 (1) (a) (c) (e) を用いた場合には、正しい値を得られないのでしょうか? 条件に設定する値などを変えてもだめなのでしょうか? (2) 自分が作っている電卓では12桁の表示を予定しているのですが その場合、メソッド sqrt ないの cnt は while ループ内で いくつまで、インクリメントすればよいのでしょうか? (3) インターネットで調べたこのアルゴリズムは 「ニュートン法」だそうですが、 y = x^2 - C の グラフ、接線などを書いてみて ある程度理解できたのですが、 http://cpplover.blogspot.jp/2010/11/blog-post_20.html 上記サイトではこのアルゴリズムは バビロニア人の方法(Babylonian method) というものだそうですが、 バビロニア人は二次関数の微分はわかっていた、 ということでしょうか? -------------------------------------------- ご存知のかた、教えていただけないでしょうか? よろしくお願いします。

noname#173931
noname#173931
  • Java
  • 回答数3
  • ありがとう数2

質問者が選んだベストアンサー

  • ベストアンサー
  • hirotn
  • ベストアンサー率59% (147/246)
回答No.2

示したtoString()による数字の表示例は、BigDecimalクラスのリファレンスより引用したものであります。 BigDecimalは、数字と小数点の有効桁数(スケール)を情報として持ちます。 #1の回答時は、一度toStringで渡しているため、スケールの情報も正しく渡るのか否か疑問がありました。クラスリファレンスの例のように帰ってしまうと2~3桁くらいで丸まってしまわないか、と。 混乱させてしまい申し訳ありません。 ----- 再現して確認してみましたが、(a)(c)(e)の場合、 条件判定に使うgapの作り方 gap = x.subtract( last_x );//(e) ループ終了判定 while( gap.compareTo(range) > 0 )//(c) この条件がよくなさそうです。この条件によって、ループを2回しか回っていないので結果の精度が良くない。(b)(d)(f)の場合でも、ループを2回実行した場合の結果は、(a)(c)(e)と同様でした。 (1)条件判定に用いるgapの作り方 gap = x.subtract( last_x );//(e) Xn (プログラム上変数x) と、 Xn-1 (同じくlast_x) の大小関係が保証できないので(一回目のxに依存する)、last_xとxの変化量は絶対値をとった方が良いでしょう。理由はrangeは常にプラスだからです。差分gapがマイナスの場合に不具合が出ます。これが今回、(c)においてループが2回で終わってしまった直接の原因です。 (2)ループ終了判定 (b)(d)(f)では、ループを必ず20回回るので、その分平方根の精度は良くなります。 確認方法は cnt ++; //(f) の下の行に以下のprintを追加して確認しました(toString()は省略できる)。 System.out.println(cnt + " Scale: lastx: " + last_x.scale() + "value: "+ last_x); System.out.println(cnt + " Scale: t: " + t.scale() + "value: "+ t); System.out.println(cnt + " Scale: x: " + x.scale() + "value: " + x); System.out.println(cnt + " Scale: gap: " + gap.scale() + "value: " + gap); ループ20回目のgapは、20 Scale: gap: 81 value: -2.5806427001953125E-65 すなわち小数点以下81桁まで有効で、-2.58*10^(-64)乗。rangeは10^(-11)乗なので、全然小さい。 (1)(2)より、このように変えてみました。 BigDecimal range = new BigDecimal(1.0e-64);//(a) while (gap.abs().compareTo(range) > 0) //(c') 結果: 7Scale: lastx: 67value: 1.732050807568877293527446341505872366942805253810380628 0558069794125 7Scale: t: 64value: 1.732050807568877293527446341505872366942805253810380628 0558069794 7Scale: x: 68value: 1.732050807568877293527446341505872366942805253810380628 05580697940625 7Scale: gap: 68value: -6.25E-66 sqrt(3) = 1.73205080756887729352744634150587236694280525381038062805580697940625 ループ7回で抜けてくれました。

その他の回答 (2)

  • f272
  • ベストアンサー率46% (8012/17124)
回答No.3

(1) 正しい値が求められないのはアルゴリズムが間違っているせいです。 gap = x.subtract( last_x );//(e) を gap = x.subtract( last_x ).abs();//(e) にしましょう。 (2) それはvalueの値によります。 cntの値で制御するよりも、しっかりと必要な精度を満たしているかどうかを確認すべきでしょう。 (3) 微分という概念はわからなくても、このようなアルゴリズムは発見できるでしょう。 √Cを求めるということはC=(a+b)^2となるようなa+bを求めることですが a^2がCに近ければbは非常に小さな数になります。このとき C=(a+b)^2=a^2+2ab+b^2=a^2+2ab (b^2は非常に小さな数だから) となって b=(C-a^2)(2a)=C/(2a)-a/2 で √C=a+b=a/2+C/(2a)=(a+C/a)/2 ですね。 微分を知らなくても大丈夫です。

noname#173931
質問者

お礼

回答していただき、ありがとうございます。 (1) 絶対値をとるのですか。 変数の内容を確認するべきでした。 わからなかったかもしれないですが・・・ (2) (a)(c)(e) の組み合わせで正しいアルゴリズムを用いる べきなのですね。 貴重な時間をさいての回答ありがとうございました。

  • hirotn
  • ベストアンサー率59% (147/246)
回答No.1

last_x = new BigDecimal( x.toString() );(**) →last_x.clone(x); (a)(c)(e)の場合、ここで精度が落ちていないか気になります。 BigDecimal.toString()が返す値は、例えば、以下の様な値だからです。 [123,0] "123" [-123,0] "-123" [123,-1] "1.23E+3" [123,-3] "1.23E+5" [123,1] "12.3" [123,5] "0.00123" [123,10] "1.23E-8" [-123,12] "-1.23E-10" 逆に、(b)(d)(f)の方法の場合は、(**)で精度を壊さずに(e)でlast_x を使っているので問題ないのかと推測。

noname#173931
質問者

お礼

さっそく回答していただきありがとうございます。 解説していただけたところによると、 toString() で返される文字列は IEEE754 などでの、仮数部、指数部の情報を 持っているものであり、 仮数部の表示桁数に制限があるための 丸め誤差が生じる ということでよろしいでしょうか? おそれいりますが、もう少し教えていただけないでしょうか? よろしくお願いします。

関連するQ&A

  • java 電卓 平方根

    こんにちは。趣味でプログラミングをしているものです。 さっそくですが質問させていただきます。 Java で文具店で1000円くらいで売っているような電卓を再現 しようとしているのですが 平方根のボタンを押したときの処理でつまづいています。 3 を入力して平方根のボタンを押すと以下のサイトに表示されている限りにおいては 同じ数値になります。 http://ja.wikipedia.org/wiki/3%E3%81%AE%E5%B9%B3%E6%96%B9%E6%A0%B9 ここで質問の内容について説明させていただきます。 Windows に標準で付属している電卓では 0.5 を入力して平方根のボタンを押し続けると 最後には 1 が表示されます。 ですが、以下に示す自分のコードだと 0.99999999998 と表示されてその後 平方根のボタンを押し続けても同じ結果になります。 0.99999999998 で収束しているのだと思いますが、 (自分の作った電卓では小数点以外の数字は12個表示されるようになっています。) 1 に収束するようにするには、 以下に示す メソッドsqrt 内の計算部分の 割り算のスケールの指定とかを変えれば うまくいくのでしょうか? ----------------------------------------------- 平方根のボタンを押したときの処理です。 case CalculatorEvent.TYPE_SQRT : if( negativeflag ) { if( ! display.showBigMinus ) showNumString = showNumString.substring( 1 ); } result = sqrt( new BigDecimal( showNumString ) ); //test statement System.out.println( "from 1446 : " + result.toString() ); result = result.setScale( 32, RoundingMode.HALF_UP ); showNumString = result.toString(); //test statement; System.out.println( "sqrt result : " + showNumString ); // . . . . //以下おもに端数を表示桁数に丸めて、 //負の値の平方根を求めたときは //絶対値の平方根を求めて表示 // エラーであるマークも表示 // 呼び出し元に例外を投げる // 呼び出した側で例外を受け取ると // 電卓のボタンを入力できないようにする ----------------------------------------------- 上記コード test statement は以下の出力結果になると 以後 平方根のボタンを押しても同じ結果が出力されます。 from 1446 : 0.9999999999899999999999499999999994999999999937499999999125000000078125 sqrt result : 0.99999999998999999999995000000000 ------------------------------------------------ 上記コード中のメソッド sqrt です。 private BigDecimal sqrt( BigDecimal value ) { BigDecimal two = new BigDecimal( "2" ); BigDecimal x = value.divide( two ); BigDecimal last_x = BigDecimal.ZERO; BigDecimal gap = x.subtract( last_x ); BigDecimal range = BigDecimal.ONE.movePointLeft( 64 ); BigDecimal t; while( gap.compareTo(range) > 0 ) { last_x = new BigDecimal( x.toString() ); //t = value.divide( x, 64, BigDecimal.ROUND_DOWN ); //test statement t = value.divide( x, 64, RoundingMode.HALF_EVEN ); // x = x.add( t ).divide( two ); gap = x.subtract( last_x ).abs(); } return x; } ----------------------------------------------- 長文になりもうしわけありませんが、 ごぞんじのかた、教えていただけないでしょうか? よろしくお願いします。

    • ベストアンサー
    • Java
  • Java 電卓を作っているのですが・・・

    こんにちは。 趣味でプログラミングをしているものです。 Java で電卓を再現しようとしていて、 コーディングが終わり動作を確認しているのですが、 わからないところがあるので質問させていただきました。 よろしくおねがいします。 平方根を求めるときなのですが、3や5などはある程度正確に求められている と思うのですが、 平方根を求めたとき整数になるもの 例えば9などは小数のまま表示されてしまいます。 電卓で表示され得るすべての実数について平方根を求めるには ニュートン法がよいと思うのですが・・・ 二点ほど質問させていただきます。 ------------------------------------ 質問A まず、BigDecimal型の平方根を求めて、 その toString() から得られた文字列を メソッドvalidControl( String value ) に渡すのですが その冒頭で末尾から先頭方向に続く'0' の列を取り除きたいのですが そのために、変数の中身をみてみたのがこの質問の下部のほうにある (ア) (イ) です。 sqrt(() などの演算結果でえられた文字列のうち 信頼できる範囲以下の文字列を validControl で切り取りたいのですが プロが実際に電卓を作るとしたら、 どのような処理になるのでしょうか? ------------------------------------ 質問B 平方根を求めるメソッドsqrt 内で二分法の処理では 除算のスケールを 64 に指定していますが、 実行結果(ア)(イ)の文字列の長さが異なるのは なぜでしょうか? ------------------------------------ private BigDecimal sqrt( BigDecimal value ) { BigDecimal two = new BigDecimal( "2" ); BigDecimal x = value.divide( two ); BigDecimal last_x = BigDecimal.ZERO; BigDecimal gap = x.subtract( last_x ); BigDecimal range = BigDecimal.ONE.movePointLeft( 64 ); BigDecimal t; while( gap.compareTo(range) > 0 ) { last_x = new BigDecimal( x.toString() ); //t = value.divide( x, 64, BigDecimal.ROUND_DOWN ); //(イ) //test statement t = value.divide( x, 64, RoundingMode.UP ); // (ア) // x = x.add( t ).divide( two ); gap = x.subtract( last_x ).abs(); } return x; } -------------------------------------- private String validControl( String value ) throws KETAException { String retstring = ""; boolean overflag = false; int idx1 = value.indexOf( '.' ); int gap = 0; int seisu_length = 0; // 整数部分の文字列の長さ int len = value.length(); int start = len-1; int idx2 = start; System.out.println( "(0)" + value ); System.out.println( "(1)" + String.valueOf( start ) ); while( value.charAt( idx2 ) == '0' ) { idx2 --; } if( idx2 != start ) { value = value.substring( 0, idx2 + 1 ); len = value.length(); } System.out.println( "(2)" +String.valueOf( idx2 ) ); // 以下省略 -------------------------------------- (ア) を実行したときの結果 (0) 3.000000000000000000000000000000000000 00000000000000000000000000000546875 (1)72 (2)72 --------------------------------------- (イ) を実行したときの結果 (0) 2.999999999999999999999999999999999999 999999999999999999999999999996875 (1)70 (2)70 -------------------------------------- 長文になり申しわけありませんが ご存知のかた教えていただけないでしょうか? よろしくお願いします。

    • ベストアンサー
    • Java
  • 平方根 応用問題がわからず困っています

    次の2つの平方根についての問題がわからず困っています √40a の値が2桁の自然数になるような、自然数aの値をすべて求めなさい。 連続する3つの自然数a,b,cがある。√2+3+4 の値は、√9=3のように整数になるが、このように、√a+b+c の値が整数となるa,b,cの組の求め方を書きなさい。 解き方(考え方)がわかるように、途中式や説明もいただければ、幸いです。 よろしくお願い致します。

  • 実数と平方根

    まちゅと言います、おそらく高校1年の初めの範囲ではないかと思うのですが、実数と平方根が分からないので教えていただきたいです。 Q1: x=2a-1のとき、√x+8aをaの式で表せ。 答えは 2a+1(a≧-1/2),-2a-1(a<-1/2) となっているのですが、 |2a+1|を出すまではわかるのですが、(a≧-1/2),-2a-1(a<-1/2) の部分が何を言っているのかさっぱりわかりません。 まず、この問題は何をだそうとしてるのかを教えてください。 上記の問題の前のページにある問題で Q2 :|x-2|=3を満たす実数xの値を求めよ。 と言うのがあり、|a|=3(aの絶対値3)を満たすのは何と何か、という 問題で、 答えが x=5、-1 なのですが、これは3という絶対値はxが5の時と-1の時に 満たされると言うことでよいのでしょうか? またQ1と基本的に同じなのでしょうか? よろしくお願いいたします。

  • BASIC言語の問題(初心者)

    こんばんは(>_<) 学校からの課題で非常に困っています!!!! BASIC言語の仕組みは読めても、どうしても組み立てが出来ないのです。。。 *1* 2の平方根を出力しなさい(二分法による平方根の計算) ただしδ=0.0001とする *2* 2の平方根を出力しなさい(ニュートン・ラプソン法による平方根の計算) ただしδ=0.0001とする ヒントとして、それぞれのアルゴリズム(?)があるので紹介します(>_<) *1*(xの平方根を精度δで求める) a←0 b←x while b-a < δ do c←(a+b)/2 if C*C > x then b←c else a←c endif done return a *2*(xの平方根を精度δで求める) y←x while │x-y*y│ < 2yδ do y←(y*y+x)/2y done return y 自分は文系で、パソコンの知識まったくないので、 どうかお助け下さいo(;△;)o涙

  • 数Iの食塩水の問題と、平方根の問題教えてください。

    数Iの食塩水の問題と、平方根の問題教えてください。 (1) 1%の食塩水600gから((1))gの水を蒸発させれば2%の食塩水になる。更に、食塩を((2))g加えれば4%の食塩水が出来る。 解) (1) まず、食塩の重さを計算 600*1/100=6(g) (1)をxとして、 6/(600+x)*100=2(%) 600=2(600+x) 2x=600 x=300 (2)答えは25/4なのですが解方法が分かりません。 (2) a=3/√5+√2、b=3/√5-√2のとき、a^2+b^2の値を求めよ。答えは14ですが私が計算すると 以下のようになってしまいます。 見づらいとは思いますが、どこが間違って14にならないのかご指摘お願いします。 また、両方共宿題ではなく、過去問やってます。既に社会人ですが、やりたい事があって数年ぶりに受験します。 (3/√5+√2)^2=9/7 (3/√5-√2)^2=9/3 9/7+9/3 =27/21+63/21 =90/21

  • 青チャート【数学I】平方根と式の値(2)について

    青チャート【数学I】 p42 基本例題24【平方根と式の値(2)】 の問題(3)についてなんですけど x+1/x = √5 のとき、次の式の値を求めよ。 x*4 + 1/x*4 と言う問題なのですが、 (PCでの書き方が解らないので妙で解りづらい書き方になってすいません *を累乗のマークとしました x*4 = xの4乗 1/x*4というのは xの4乗 分の 1 と言うことです) 解答を見ると x4 + 1/x4 = (x*2)*2 + 1/(x*2)*2 = (x*2 + 1/x*2)*2 - 2 = 3*2 - 2 = 7 となっているのですが、一体どうやったら、 x+1/x = √5 で (x*2 + 1/x*2)*2 が 3*2 になるのかが解らなくて困っています。 計算の行程を教えて貰えるとありがたいです。 あとPCでこういった数式を表現するのか分かるサイトなどを教えていただけると更にありがたいです。

  • 電卓アプリで%キーの計算プログラムについて

    現在、Androidのアプリ作成の勉強に励んでいます。 まず、手始めに電卓アプリを作りながら学んでいますが、 電卓キーの「%キー」をClickした時の計算プログラムがどうしても考えつきません。 できれば、BigDecimalメソッドを使って「%キー」の計算をさせることができればと 思っています。 doCalcというメソッドを作ってその中に計算プログラムを書いていて doCalcメソッドの中身(足し算、引き算、乗算、除算についての計算プログラム)についての計算プログラム)は下記のとおりです。 是非、ご教授いただければ助かります。 またBigDecimalメソッドを使わず他のやりかたでも結構ですので、参考にご教授いただければ幸いです。 宜しくお願いいたします。 //strResult=入力中の数値 //strTemp=入力が完了した数値と計算結果で共有 private String doCalc() { BigDecimal bd1=new BigDecimal(strResult); BigDecimal bd2=new BigDecimal(strTemp); BigDecimal result=BigDecimal.ZERO; switch(operator) { case R.id.KeypadAdd: result=bd1.add(bd2); break; case R.id.KeypadSub: result=bd1.subtract(bd2); break; case R.id.KeypadMulti: result=bd1.multiply(bd2); break; case R.id.KeypadDiv: if(!bd2.equals(BigDecimal.ZERO)){ result=bd1.divide(bd2, 12, 3); }else{ Toast toast=Toast.makeText(this,R.string.toast_div_by_zero,1000); toast.show(); } break; } if(result.toString().indexOf(".")>=0){ return result.toString().replaceAll("\\.0+$|0+$",""); }else{ return result.toString(); } }

  • この円周率を求める式について・・・

    まず、「X=√3」とします。 その「X」に「2」を加えます。 つまり、「X=2+√3」となります。 その値の平方根を求めます。 つまり、「√(2+√3)」となります。 その値を、「X」に代入します。 つまり、「X=√(2+√3)」となります。 その「X」に「2」を加えます。 つまり、「X=2+√(2+√3)」となります。 その値の平方根を求めます。 つまり、「√(2+√(2+√3))」となります。 その値を、「X」に代入します・・・。 この「X」に「2」を加えて平方根を求めることを、適当な回数繰り返します。(繰り返した回数を「N」とします。) 続いて、上記の計算の答えの「X」と「N」を次の式「2^(N+1)×3×√(2-X)」に当てはめます。 すると、繰り返す回数が多いほど円周率の「3.1415…」に 近づくのですが、これと「まったく同じ」という円周率の公式はあるのでしょうか?あるとしたら、公式の名前を教えてください。 わかりにくい質問でごめんなさい。 ちなみに、実際に計算した場合、「N=2」のとき(「2」を加えて平方根を求めることを2回繰り返したとき)は「X=1.9828897・・・」となり、次の式に値を代入すると「2^(2+1)×3×√(2-1.9828897)」、答えは「3.139352・・・」となります。

  • 高校の数学の問題です

    二次方程式を平方根による解法で解け。 ((x-3)^2=7             x^2+8x+6=0     √5=2.236とするとき3-√5                                            分の4の値を求めよ よろしくお願い致します                                   

専門家に質問してみよう