而不是教您怎么着准确地、科学地用java求出函数在好几的导数,而不是教您怎么着规范地、科学地用java求出函数在少数的导数

一、引子

 

def d(f) :
    def calc(x) :
        dx = 0.000001  # 表示无穷小的Δx
        return (f(x+dx) - f(x)) / dx  # 计算斜率。注意,此处引用了外层作用域的变量 f
    return calc  # 此处用函数作为返回值(也就是函数 f 的导数)
# 计算二次函数 f(x) = x2 + x + 1的导数
f = lambda x : x**2 + x + 1  # 先把二次函数用代码表达出来
f1 = d(f)# 这个f1 就是 f 的一阶导数啦。注意,导数依然是个函数
# 计算x=3的斜率
f1(3)
# 二阶导数
f2 = d(f1)

首先,直接上一段python代码,请大家先分析下方面代码是用哪些方法求导的。请不要被这段代码吓到,你无需纠结它的语法,只要驾驭它的求导思路。

上述代码引用自《干什么小编推荐 Python[4]:作为函数式编制程序语言的
Python
》,那篇博客是敦促自身写篇小说的关键原因。

博主说“假若不用 FP,改用 OOP,上述供给该怎么落到实处?作者觉得啊,用 OOP
来求导,那代码写起来多半是又丑又臭。”

自己将信将疑,于是就用面向对象的java试了试,最终也没多少代码。借使用java8或之后版本,代码更少。

请我们想想2个难题,怎么样用面向对象的思路改写这么些顺序。请先好好考虑,尝试编个程序再持续往下看。

考虑到看到那个标题进来的同室大多是学过java的,上面小编用java,用面向对象的思路一步步剖析那一个标题。

 

先是声飞鹤点,本文首要介绍的是面向对象(OO)的思想,顺便谈下函数式编制程序,而不是教您怎么样准确地、科学地用java求出函数在某个的导数。

柒 、编制程序范式

 

在笔者眼里,编制程序范式简言之来说正是编制程序的一种方式,一种风格。

本人先介绍当中的五个,你大概就精晓它的意思了。

 

7.3 进程式编制程序(Procedural programming)

约莫学过编制程序都学过C,C语言正是一种过程式编制程序语言。以作者之见,进程式编制程序大概正是为着完结2个需求,像记流水帐一样,平铺直叙下去。 

       

率先声澳优点,本文首要介绍的是面向对象(OO)的思维,顺便谈下函数式编制程序,而不是教您如何规范地、科学地用java求出函数在少数的导数。

二、求导

 

文章开端笔者已近注解过了,本文不是来切磋数学的,求导只是自我用来证实面向对象的一个事例。

假若你曾经忘了早先这段代码的求导思路,请回头再看看,看看用python是何许求导的。

信任您借使听闻过求导,肯定一眼就见到起先那段代码是用导数概念求导的。

图片 1

代码中只是将无穷小Δx粗略地算做八个较小的值0.000001。

 

③ 、最初的想法

 

//自定义函数
public class Function {
    //函数:f(x) = 3x^3 + 2x^2 + x + 1
    public double f(double x) {
        return 3 * x * x * x + 2 * x * x + x + 1;
    }
}

//一元函数导函数
public class DerivedFunction {
    //表示无穷小的Δx
    private static final double DELTA_X = 0.000001;
    //待求导的函数
    private Function function;

    public DerivedFunction(Function function) {
        this.function = function;
    }

    /**
     * 获取function在点x处的导数
     * @param x 待求导的点
     * @return 导数
     */
    public double get(double x) {
        return (function.f(x + DELTA_X) - function.f(x)) / DELTA_X;
    }
}

public class Main {
    public static void main(String[] args) {
        //一阶导函数
        DerivedFunction derivative = new DerivedFunction(new Function());
        //打印函数在x=2处的一阶导数
        System.out.println(derivative.get(2));
    }
}

先声美素佳儿(Friso)点,考虑到博客篇幅,小编动用了不规范的代码注释,希望大家不用被小编误导。

作者想只要大家好好想想了,应该至少会想到那步吧。代码笔者就不表达了,小编只是用java改写了作品开头的那段python代码,做了贰个简约的翻译工作。再请我们着想下以上代码的题目。

刚开端,作者心想那些题材想开的是建贰个名为Function的类,类中有叁个名为f的章程。但考虑到要每一次供给新的函数导数时就得改变那么些f方法的贯彻,鲜明不便于扩充,那违反了开闭原则

估摸有的同学没听过这一个词,作者就解释下:”对象(类,模块,函数等)应对扩张开放,但对修改封闭“。

于是本身就没继续写下去,但为了让我们直观的感受到这一个想法,我写那篇博客时就兑现了瞬间以此想法。

请大家想想一下怎么着重构代码以消除扩充性难题。

 

5、后来的想法

 

当自个儿写出地点的代码时,小编觉得完全可以矢口否认“用 OOP
来求导,那代码写起来多半是又丑又臭”的视角。但还无法求二阶导,笔者有点不甘心。

于是乎笔者就动笔,列了须臾间用定义求一阶导和求二阶导的架势,想了想七个姿态的界别与联络,突然想到导函数也是函数。

DerivedFunction的get方法和Function的f方法的参数和重临值一样,DerivedFunction能够兑现Function接口,于是发出了上面的代码。

public interface Function {
    double f(double x);
}

public class DerivedFunction implements Function {
    private static final double DELTA_X = 0.000001;
    private Function function;

    public DerivedFunction(Function function) {
        this.function = function;
    }

    @Override
    public double f(double x) {
        return (function.f(x + DELTA_X) - function.f(x)) / DELTA_X;
    }
}

public class Main {
    public static void main(String[] args) {
        Function f1 = new DerivedFunction(new Function() {
            @Override
            public double f(double x) {
                return 3 * x * x * x + 2 * x * x + x + 1;
            }
        });
        System.out.println(f1.f(2));
        //二阶导函数:f''(x) = 18x + 4
        Function f2 = new DerivedFunction(f1);
        //打印函数f(x) = 3x^3 + 2x^2 + x + 1在x=2处的二阶导数
        System.out.println(f2.f(2));
    }
}

考虑到一些同学没学过java8或以上版本,以上代码没有运用java8函数式编制程序的新特点。 

假使你接触过java8,请考虑怎么样改写以上代码,使其更简明。

 

7.1 面向对象程序设计(OOP)

来看此间的同班应该对面向对象有了更直观的认识。在面向对象编制程序中,万物皆对象,抽象出类的定义。基本特点是包装、继承、多态,认识不深的同校能够再去本人事先的代码中找找那两特性格。

自个儿事先还介绍了面向对象的多少个条件:开闭原则借助反转原则。其余还有单纯职分规范里氏替换原则接口隔开原则。那是面向对象的多少个为主尺度,合称SOLID)。

 

④ 、起初的想法

 

估计学过面向对象的同学会想到把Function类改成接口或抽象类,现在每一次添加新的函数时假设重写那么些接口或抽象类中的f方法,那正是面向接口编制程序,符合借助于反转原则,上边的代码正是那般做的。

再声称一点,考虑到篇幅的标题,前边的代码小编会省去与前边代码重复的笺注,有不明了的地点还请看看上二个想法中的代码。

//一元函数
public interface Function {
    double f(double x);
}

//自定义的函数
public class MyFunction implements Function {
    @Override
    public double f(double x) {
        return 3 * x * x * x + 2 * x * x + x + 1;
    }
}

public class DerivedFunction {
    private static final double DELTA_X = 0.000001;
    private Function function;

    public DerivedFunction(Function function) {
        this.function = function;
    }

    public double get(double x) {
        return (function.f(x + DELTA_X) - function.f(x)) / DELTA_X;
    }
}

public class Main {
    public static void main(String[] args) {
        //一阶导函数:f'(x) = 9x^2 + 4x + 1
        DerivedFunction derivative = new DerivedFunction(new MyFunction());
        System.out.println(derivative.get(2));
    }
}

自家想认真看的同班或者会意识三个难题,笔者的翻译做的还不成功,早先这段python代码还足以轻松地求出二阶导函数(导数的导数),而作者的代码却非凡。

事实上只要稍加修改上述代码的二个地点就能够轻松实现求二阶导,请再思索片刻。

 

⑤ 、后来的想法

 

当本人写出地点的代码时,小编觉得完全能够矢口否认“用 OOP
来求导,那代码写起来多半是又丑又臭”的看法。但还不能求二阶导,笔者有点不甘心。

于是乎本人就动笔,列了须臾间用定义求一阶导和求二阶导的架子,想了想七个姿态的界别与联系,突然想到导函数也是函数。

DerivedFunction的get方法和Function的f方法的参数和重临值一样,DerivedFunction能够完结Function接口,于是发生了上面包车型大巴代码。

public interface Function {
    double f(double x);
}

public class DerivedFunction implements Function {
    private static final double DELTA_X = 0.000001;
    private Function function;

    public DerivedFunction(Function function) {
        this.function = function;
    }

    @Override
    public double f(double x) {
        return (function.f(x + DELTA_X) - function.f(x)) / DELTA_X;
    }
}

public class Main {
    public static void main(String[] args) {
        Function f1 = new DerivedFunction(new Function() {
            @Override
            public double f(double x) {
                return 3 * x * x * x + 2 * x * x + x + 1;
            }
        });
        System.out.println(f1.f(2));
        //二阶导函数:f''(x) = 18x + 4
        Function f2 = new DerivedFunction(f1);
        //打印函数f(x) = 3x^3 + 2x^2 + x + 1在x=2处的二阶导数
        System.out.println(f2.f(2));
    }
}

设想到部分同学没学过java8或上述版本,以上代码没有应用java8函数式编制程序的新天性。 

只要你接触过java8,请考虑怎么改写以上代码,使其更简明。

 

陆 、最终的想法

 

public class DerivedFunction implements Function<Double, Double> {
    private static final double DELTA_X = 0.000001;
    private Function<Double, Double> function;

    public DerivedFunction(Function<Double, Double> function) {
        this.function = function;
    }

    @Override
    public Double apply(Double x) {
        return (function.apply(x + DELTA_X) - function.apply(x)) / DELTA_X;
    }
}

public class Main {
    public static void main(String[] args) {
        //打印函数在x=2处的二阶导
        System.out.println(new DerivedFunction(new DerivedFunction(x -> 3 * x * x * x + 2 * x * x + x + 1)).apply(2.0));
    }
}

前边多少个想法为了扩展Function接口,使用了表面类、匿名类的点子,其实也得以用个中类。而那在此处,笔者用了lambda表明式,是否更简短了。

此地用的Function接口用的是jdk自带的,大家不需求自个儿定义了。因为那是贰个函数式接口,大家得以用lambda方便地贯彻。后来发现,其实那里用UnaryOperator那几个接口更伏贴。

未来我们有没有觉察,用java、用OOP也得以11分简洁地完结求导,并不如起先的那段python代码麻烦很多。

 

7.2 函数编制程序语言(FP)

本文开首那段代码用的就是python函数式编制程序的语法,后来作者又用java8函数式编制程序的语法翻译了那段代码。

深信不疑您早就直观地感受到它的精简,以函数为核心,几行代码就化解了求导的难点。

 

7、编制程序范式

 

以小编之见,编制程序范式粗略来说正是编制程序的一种形式,一种风格。

笔者先介绍在那之中的七个,你差不离就知道它的意义了。

 

八、结尾

 

是因为我初学java,近日不得不想到这么多。如若大家有更好的想法依旧觉的小编上面说的不通常,欢迎评论,望各位不吝赐教。

那是自小编的第1篇技术博客,但愿自个儿说了然了面向对象。要是对您有支持,请点个赞只怕评论下,给本人点持续创作的引力。

八、结尾

 

由于自个儿初学java,近日只得想到这么多。假若我们有更好的想法照旧觉的本身上边说的有标题,欢迎评论,望各位不吝赐教。

那是本人的首先篇技术博客,但愿小编说明白了面向对象。假如对你有支持,请点个赞也许评论下,给本身点持续创作的重力。

二、求导

 

小说开首笔者已近申明过了,本文不是来商量数学的,求导只是本身用来验证面向对象的三个例证。

万一您早已忘了开首那段代码的求导思路,请回头再看看,看看用python是如何求导的。

深信不疑你一旦据他们说过求导,肯定一眼就见到开始那段代码是用导数概念求导的。

图片 2

代码中只是将无穷小Δx粗略地算做三个较小的值0.000001。

 

 

 

一、引子

 

def d(f) :
    def calc(x) :
        dx = 0.000001  # 表示无穷小的Δx
        return (f(x+dx) - f(x)) / dx  # 计算斜率。注意,此处引用了外层作用域的变量 f
    return calc  # 此处用函数作为返回值(也就是函数 f 的导数)
# 计算二次函数 f(x) = x2 + x + 1的导数
f = lambda x : x**2 + x + 1  # 先把二次函数用代码表达出来
f1 = d(f)# 这个f1 就是 f 的一阶导数啦。注意,导数依然是个函数
# 计算x=3的斜率
f1(3)
# 二阶导数
f2 = d(f1)

先是,直接上一段python代码,请大家先分析下方面代码是用怎么着艺术求导的。请不要被那段代码吓到,你无需纠结它的语法,只要领会它的求导思路。

上述代码引用自《缘何我推荐 Python[4]:作为函数式编制程序语言的
Python
》,那篇博客是敦促本身写篇小说的第②原因。

博主说“要是不用 FP,改用 OOP,上述须求该怎么贯彻?小编觉得呢,用 OOP
来求导,那代码写起来多半是又丑又臭。”

本人将信将疑,于是就用面向对象的java试了试,最终也没多少代码。若是用java8或之后版本,代码更少。

请我们想想1个题材,怎么样用面向对象的思绪改写那么些顺序。请先好好考虑,尝试编个程序再持续往下看。

考虑到看到这些标题进来的同桌大多是学过java的,上边小编用java,用面向对象的思绪一步步剖析那么些题目。

 

肆 、初始的想法

 

估计学过面向对象的同学会想到把Function类改成接口或抽象类,未来每趟添加新的函数时假使重写这一个接口或抽象类中的f方法,那正是面向接口编制程序,符合依靠反转原则,上面包车型地铁代码正是如此做的。

再声爱他美点,考虑到篇幅的题材,前边的代码小编会省去与前边代码重复的笺注,有不知晓的地点还请看看上三个想方设法中的代码。

//一元函数
public interface Function {
    double f(double x);
}

//自定义的函数
public class MyFunction implements Function {
    @Override
    public double f(double x) {
        return 3 * x * x * x + 2 * x * x + x + 1;
    }
}

public class DerivedFunction {
    private static final double DELTA_X = 0.000001;
    private Function function;

    public DerivedFunction(Function function) {
        this.function = function;
    }

    public double get(double x) {
        return (function.f(x + DELTA_X) - function.f(x)) / DELTA_X;
    }
}

public class Main {
    public static void main(String[] args) {
        //一阶导函数:f'(x) = 9x^2 + 4x + 1
        DerivedFunction derivative = new DerivedFunction(new MyFunction());
        System.out.println(derivative.get(2));
    }
}

自身想认真看的同学大概会发现几个标题,小编的翻译做的还不做到,起初这段python代码还能轻松地求出二阶导函数(导数的导数),而本身的代码却非凡。

实际只要稍加修改上述代码的三个地点就能够轻松达成求二阶导,请再思考片刻。

 

7.1 面向对象程序设计(OOP)

看到那里的同窗应该对面向对象有了更直观的认识。在面向对象编制程序中,万物皆对象,抽象出类的定义。基本特点是包装、继承、多态,认识不深的同桌能够再去本身事先的代码中找找那多个特点。

本人在此之前还介绍了面向对象的多少个规范:开闭原则依傍反转原则。其余还有单纯任务规范里氏替换原则接口隔开原则。那是面向对象的陆个基本原则,合称SOLID)。

 

7.3 进程式编制程序(Procedural programming)

大致学过编程都学过C,C语言正是一种进程式编制程序语言。以笔者之见,进程式编制程序大致便是为了做到叁个急需,像记流水帐一样,平铺直叙下去。 

       

三 、最初的想法

 

//自定义函数
public class Function {
    //函数:f(x) = 3x^3 + 2x^2 + x + 1
    public double f(double x) {
        return 3 * x * x * x + 2 * x * x + x + 1;
    }
}

//一元函数导函数
public class DerivedFunction {
    //表示无穷小的Δx
    private static final double DELTA_X = 0.000001;
    //待求导的函数
    private Function function;

    public DerivedFunction(Function function) {
        this.function = function;
    }

    /**
     * 获取function在点x处的导数
     * @param x 待求导的点
     * @return 导数
     */
    public double get(double x) {
        return (function.f(x + DELTA_X) - function.f(x)) / DELTA_X;
    }
}

public class Main {
    public static void main(String[] args) {
        //一阶导函数
        DerivedFunction derivative = new DerivedFunction(new Function());
        //打印函数在x=2处的一阶导数
        System.out.println(derivative.get(2));
    }
}

先声可瑞康(Beingmate)点,考虑到博客篇幅,小编利用了不标准的代码注释,希望大家不要被笔者误导。

自个儿想只要大家能够考虑了,应该至少会想到那步吧。代码小编就不表达了,小编只是用java改写了小说初始的那段python代码,做了一个大致的翻译工作。再请大家着想下以上代码的难题。

刚伊始,作者思想这一个标题想开的是建一个名为Function的类,类中有二个名为f的法子。但考虑到要每一次要求新的函数导数时就得改变这一个f方法的完结,分明不便宜扩大,那违反了开闭原则

预计有的同学没听过这么些词,作者就分解下:”对象(类,模块,函数等)应对扩大开放,但对修改封闭“。

于是乎小编就没继续写下去,但为了让大家直观的感想到那个想法,小编写这篇博客时就落实了一下以此想法。

请大家想想一下哪些重构代码以缓解扩展性难点。

 

6、最终的想法

 

public class DerivedFunction implements Function<Double, Double> {
    private static final double DELTA_X = 0.000001;
    private Function<Double, Double> function;

    public DerivedFunction(Function<Double, Double> function) {
        this.function = function;
    }

    @Override
    public Double apply(Double x) {
        return (function.apply(x + DELTA_X) - function.apply(x)) / DELTA_X;
    }
}

public class Main {
    public static void main(String[] args) {
        //打印函数在x=2处的二阶导
        System.out.println(new DerivedFunction(new DerivedFunction(x -> 3 * x * x * x + 2 * x * x + x + 1)).apply(2.0));
    }
}

事先多少个想法为了扩张Function接口,使用了外部类、匿名类的法门,其实也能够用当中类。而那在此地,作者用了lambda表达式,是或不是更不难了。

那里用的Function接口用的是jdk自带的,我们不要求团结定义了。因为那是三个函数式接口,咱们能够用lambda方便地促成。后来意识,其实那里用UnaryOperator其一接口更合适。

今昔大家有没有觉察,用java、用OOP也能够万分简短地落到实处求导,并不及开始的这段python代码麻烦很多。

 

7.2 函数编制程序语言(FP)

正文初阶那段代码用的正是python函数式编制程序的语法,后来本身又用java8函数式编制程序的语法翻译了那段代码。

信任你已经直观地感受到它的简练,以函数为基本,几行代码就消除了求导的题目。

 

相关文章