1. 程式人生 > >Java練習:牛頓迭代法 Vs. 不動點

Java練習:牛頓迭代法 Vs. 不動點

SICP中,說明了功能抽象的一種型別:一般化函式的設計。通用函式在通常的Java/C語言的教學中,通常放在後面作為高階內容。如在講解C的函式指標、Java的模板方法模式的時候。

1.求平方根

牛頓的逐步逼近方法是通用的方法。例如,求一個數n的平方根,先給出一個猜測值x(如初始化為1或n),如果n- x* x不滿足精度(設定精度引數),按照某個規則例如x =( x + n/ x)/2再猜測,直到滿足精度。

package semantics.statement;
import static tips.Print.*;
/**
 *  牛頓迭代逼近法
 * 
 * @author (yqj2065) 
 * @version (2016.6)
 */
public class NewtonMethod{
    static double  epsilon = 1e-15;// relative error tolerance    
    public static double sqrt(double n) {
        double x  = 1.0;//x不能夠為0,下面x作為分母;修改為while語句,則可以令 x  = n;
        do{
            x = (x + n/x)/2.0;//
        }while( Math.abs(x*x-n ) >epsilon);
        return x;
    }
    public static void test(double x){
         pln("sqrt("+x +")="+sqrt(x) );
    }
}
滿足精度的條件 Math.abs(x*x-n ) >epsilon,對於很小的數,精度不夠因而不能計算出正確的答案;對於特別大的數,因為小數精度不足以表示兩個大數之間的差,所以sqrt 會陷入死迴圈。這些我們現在不予理會。 求一個數n的平方根,即求f(x) = x2-n =0的解。而任意的f(x)可以使用泰勒級數表示。 牛頓迭代法就是利用了泰勒公式的前兩項和,是泰勒公式的簡化。 xn+1xnf(xn) /f(xn)
  • 按照簡化的泰勒公式,對於f(x) = x2-n,xn+1=xn xn2-n)  /2xn= xn2+n)  /2xn 因此猜測值的變化規則為
    x = (x + n/x)/2.0;
  • 牛頓逼近法與數列/級數求和的關係?任意的f(x)可以使用泰勒級數表示,兩者本質上一致。但是在不方便採用item與sum的方式計算時,簡化的牛頓迭代法容易獲得猜測值的變化規則,也容易比較f(x)>精度.
假設f(x) = x3-n,猜測值的變化規則xn+1x(x3-n) /(3x2)(2x3+n) /(3x2)(2x+n/x2) /3,立即可得
    public static double cube_root(double n) {
        double x  = n;//
        while (Math.abs(x*x*x-n ) >epsilon) {
            x = (2*x + n/(x*x))/3.0;//pln(x);
        }
        return x;
    }

2.求方程的解

牛頓逼近法可以用於求方程的解。一個數n的平方根,其實是方程 x2-n=0的正數解(而且n被引數化)。假設求f(x) = x3-2x-3的某個根(注意,它可能有3個實根,我們沒有指定求1和2之間的根)。
    public static double f1() {
        double x  = 0;//
        while (Math.abs(x*x*x-2*x-3 ) >epsilon) {
            x = (2*x*x*x + 3)/(3*x*x-2);//pln(x);
        }
        return x;
    }

3.不動點

不動點是指對於函式f(x),若f(n)=n。既然f(n)=n,自然有n=f(n)=f(f(n))=f(f(f(f(n))))... 那麼方程f(x)=0的解,也就是f(x)+x=x的解,或者說f(x)+x的不動點。 很容易寫出:
    public static double getFixedPoint(DoubleUnaryOperator duo){    
        double x  = 1.0;//
        while (Math.abs(x-duo.applyAsDouble(x) ) >epsilon) {
            x = duo.applyAsDouble(x);//pln(x);
        }
        return x;           
    }
雖然yqj2065覺得Java的高階函式是人妖,但是看起來像女人,那就是女人。抓狂         pln(getFixedPoint(y->Math.cos(y)));        0.7390893414033927
        pln(getFixedPoint(y->Math.sin(y)+Math.cos(y)));  1.2587228743052672
    public static void test(double x){
        pln("sqrt("+x +")="+sqrt(x) );        
        pln(getFixedPoint(y->(y+x/y)/2.0 ));        
    }
sqrt(9.0)=3.000000001396984
3.000000001396984

關於收斂問題,那是數學問題,yqj2065搞不定! 無意中看見了老早的文章:Java語言學校的危險性 “如果真是這樣,課程6.001就是你的完美選擇。你可以先講Scheme語言,這種教學語言簡單到聰明學生大約只用10分鐘,就能全部學會。然後,你將這個學期剩下的時間,都用來講解不動點。”
我們敢不敢說:用10分鐘講一下不動點,然後,你將這個學期剩下的時間,都用來講解Java API程式設計。”