python编程求导数_面向对象编程 —— java实现函数求导

首先声明一点,本文主要介绍的是面向对象(OO)的思想,顺便谈下函数式编程,而不是教你如何准确地、科学地用java求出函数在一点的导数。

一、引子

defd(f) :defcalc(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代码,请大家先分析下上面代码是用什么方法求导的。请不要被这段代码吓到,你无需纠结它的语法,只要明白它的求导思路。

博主说“如果不用 FP,改用 OOP,上述需求该如何实现?俺觉得吧,用 OOP 来求导,这代码写起来多半是又丑又臭。”

我将信将疑,于是就用面向对象的java试了试,最后也没多少代码。如果用java8或以后版本,代码更少。

请大家思考一个问题,如何用面向对象的思路改写这个程序。请先好好思考,尝试编个程序再继续往下看。

考虑到看到这个标题进来的同学大多是学过java的,下面我用java,用面向对象的思路一步步分析这个问题。

二、求导

文章开头我已近声明过了,本文不是来讨论数学的,求导只是我用来说明面向对象的一个例子。

如果你已经忘了开头那段代码的求导思路,请回头再看看,看看用python是如何求导的。

相信你只要听说过求导,肯定一眼就看出开头那段代码是用导数定义求导的。

1156638-20171201171003914-979024914.png

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

三、最初的想法

//自定义函数

public classFunction {//函数:f(x) = 3x^3 + 2x^2 + x + 1

public double f(doublex) {return 3 * x * x * x + 2 * x * x + x + 1;

}

}

//一元函数导函数

public classDerivedFunction {//表示无穷小的Δx

private static final double DELTA_X = 0.000001;//待求导的函数

privateFunction function;publicDerivedFunction(Function function) {this.function =function;

}/*** 获取function在点x处的导数

*@paramx 待求导的点

*@return导数*/

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

}

}

public classMain {public static voidmain(String[] args) {//一阶导函数

DerivedFunction derivative = new DerivedFunction(newFunction());//打印函数在x=2处的一阶导数

System.out.println(derivative.get(2));

}

}

先声明一点,考虑到博客篇幅,我使用了不规范的代码注释,希望大家不要被我误导。

我想只要大家好好思考了,应该至少会想到这步吧。代码我就不解释了,我只是用java改写了文章开头的那段python代码,做了一个简单的翻译工作。再请大家考虑下以上代码的问题。

刚开始,我思考这个问题想到的是建一个名为Function的类,类中有一个名为f的方法。但考虑到要每次要求新的函数导数时就得更改这个f方法的实现,明显不利于扩展,这违背了开闭原则。

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

于是我就没继续写下去,但为了让大家直观的感受到这个想法,我写这篇博客时就实现了一下这个想法。

请大家思考一下如何重构代码以解决扩展性问题。

四、初步的想法

估计学过面向对象的同学会想到把Function类改成接口或抽象类,以后每次添加新的函数时只要重写这个接口或抽象类中的f方法,这就是面向接口编程,符合依赖反转原则,下面的代码就是这么做的。

再声明一点,考虑到篇幅的问题,后面的代码我会省去与之前代码重复的注释,有不明白的地方还请看看上一个想法中的代码。

//一元函数

public interfaceFunction {double f(doublex);

}

//自定义的函数

public class MyFunction implementsFunction {

@Overridepublic double f(doublex) {return 3 * x * x * x + 2 * x * x + x + 1;

}

}

public classDerivedFunction {

private static final double DELTA_X = 0.000001;

privateFunction function;publicDerivedFunction(Function function) {this.function =function;

}

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

}

}

public classMain {public static voidmain(String[] args) {//一阶导函数:f'(x) = 9x^2 + 4x + 1DerivedFunction derivative= new DerivedFunction(newMyFunction());

System.out.println(derivative.get(2));

}

}

我想认真看的同学可能会发现一个问题,我的翻译做的还不到位,开头那段python代码还可以轻松地求出二阶导函数(导数的导数),而我的代码却不行。

其实只要稍微修改以上代码的一个地方就可以轻松实现求二阶导,请再思考片刻。

五、后来的想法

当我写出上面的代码时,我感觉完全可以否定“用 OOP 来求导,这代码写起来多半是又丑又臭”的观点。但还不能求二阶导,我有点不甘心。

于是我就动笔,列了一下用定义求一阶导和求二阶导的式子,想了想两个式子的区别与联系,突然想到导函数也是函数。

DerivedFunction的get方法和Function的f方法的参数和返回值一样,DerivedFunction可以实现Function接口,于是产生了下面的代码。

public interfaceFunction {double f(doublex);

}

public class DerivedFunction implementsFunction {

private static final double DELTA_X = 0.000001;

privateFunction function;publicDerivedFunction(Function function) {this.function =function;

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

}

}

public classMain {public static voidmain(String[] args) {Function f1= new DerivedFunction(newFunction() {

@Overridepublic double f(doublex) {return 3 * x * x * x + 2 * x * x + x + 1;

}

});

System.out.println(f1.f(2));//二阶导函数:f''(x) = 18x + 4

Function f2 = newDerivedFunction(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{private static final double DELTA_X = 0.000001;private Functionfunction;public DerivedFunction(Functionfunction) {this.function =function;

}

@OverridepublicDouble apply(Double x) {return (function.apply(x + DELTA_X) - function.apply(x)) /DELTA_X;

}

}

public classMain {public static voidmain(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.1 面向对象程序设计(OOP)

看到这里的同学应该对面向对象有了更直观的认识。在面向对象编程中,万物皆对象,抽象出类的概念。基本特性是封装、继承、多态,认识不深的同学可以再去我之前的代码中找找这三个特性。

我之前还介绍了面向对象的几个原则:开闭原则、依赖反转原则。其他还有单一职责原则、里氏替换原则、接口隔离原则。这是面向对象的5个基本原则,合称SOLID。

7.2 函数编程语言(FP)

本文开头那段代码用的就是python函数式编程的语法,后来我又用java8函数式编程的语法翻译了这段代码。

相信你已经直观地感受到它的简洁,以函数为核心,几行代码就解决了求导的问题。

7.3 过程式编程(Procedural programming)

大概学过编程都学过C,C语言就是一种过程式编程语言。在我看来,过程式编程大概就是为了完成一个需求,像记流水帐一样,平铺直叙下去。

八、结尾

由于本人初学java,目前只能想到这么多。如果大家有更好的想法或者觉的我上面说的有问题,欢迎评论,望各位不吝赐教。

这是我的第一篇技术博客,但愿我说清楚了面向对象。如果对你有帮助,请点个赞或者评论下,给我点继续创作的动力。

九、两年后的思考

以下是写了这篇文章两年后的思考,从评论区复制而来:

两年前我只是大概理解了OOP,无意中使用了闭包定义了层次性数据,做了数据抽象。

5000多的阅读量没人指出如果用Java8写出函数式的版本。

其实Java8引入了Lambda后已经可以写出函数式的版本,只要把文章开头的python代码翻译下。

想想“求导”这个函数,它接收一个函数做为参数,然后返回一个新的函数,这是一个高阶函数,可以用做过程抽象。

Java8引入Lambda后,函数可以作为方法参数和返回值,这是一个很重大的改变,颠覆了老Java用户原有的编程观念。

以前很复杂的OOP代码,如果用函数式方式来写,会很简单。

很多所谓的设计模式,在我看来不过是因为以前Java不支持Lambda这特性,去曲折地实现罢了,比如模板模式。

然而Lambda并不是什么新鲜的东西,Lisp这一第二古老的高级语言就支持。

这推荐下SICP这本书和公开课,我也是看了点才有了下面的代码。

有空重写一篇博客谈一谈。

public classDerivativeUtil {private static final double DELTA_X = 0.000001;public staticDoubleUnaryOperator d(DoubleUnaryOperator f) {return x -> (f.applyAsDouble(x + DELTA_X) - f.applyAsDouble(x)) /DELTA_X;

}public static voidmain(String[] args) {

DoubleUnaryOperator f= x -> x * x + x + 1;

DoubleUnaryOperator f1=d(f);

System.out.println(f1.applyAsDouble(3));

DoubleUnaryOperator f2=d(f1);

System.out.println(f2.applyAsDouble(1));

}

}

我是先用scheme翻译了下, 然后想到了上面的Java实现。编程语言不过是各种语言特性的排列组合罢了。

(define dx 0.000001)

(define (d f)

(lambda(x)

(/ (- (f (+x dx))

(f x))

dx)))

(define (f x)

(+ (* x x) x 1))

(define f1 (d f))

(f13)

(define f2 (d f1))

(f21)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/365937.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BZOJ5093图的价值(斯特林数)

题目描述 “简单无向图”是指无重边、无自环的无向图(不一定连通)。一个带标号的图的价值定义为每个点度数的k次方的和。给定n和k,请计算所有n个点的带标号的简单无向图的价值之和。因为答案很大,请对998244353取模输出。题解因为…

python程序写诗_用Python作诗,生活仍有诗和远方

原标题:用Python作诗,生活仍有诗和远方 报 名 来源:TheodoreXu链接: https://segmentfault.com/a/1190000013154329 常听说,现在的代码,就和唐朝的诗一样重要。 可对我们来说,写几行代码没什么&…

华为鸿蒙手机beta版,鸿蒙2.0 Beta手机版来了!明年将全面支持华为手机

读创/深圳商报记者陈 姝备受关注的华为鸿蒙操作系统(HarmonyOS,以下简称鸿蒙)有了新进展。华为消费者业务软件部总裁王成录日前透露,将于12月16日在北京发布鸿蒙2.0手机开发者Beta版本。王成录在12月14日发微博称:“HarmonyOS正沿着我们在HDC…

Django 路由层

Django的下载与基本命令 下载Django:pip3 install django2.0.1创建一个django project: django-admin startproject luffy在mysite目录下创建应用:python manage.py startapp app01启动django项目:python manage.py runserver 8080 我们访问:…

caffe安装_目标检测之caffe-ssd模型训练与测试

最近把一个ssd网络的net..prototxt网络结构和自己生成的hdf5格式数据一起做训练时发现经常报错,因为ssd中一些层在caffe中并没有实现,需要自己写相应的.cpp,.cu文件重新编译,比较麻烦,而大家通常训练caffe-ssd都是基于原作者公开的…

Python之classmethod和staticmethod的区别

python中3种方式定义类方法,常规方式、classmethod修饰方式、staticmethod修饰方式。 class A(object):def foo(self, x):print(调用foo函数 (%s, %s)%(self, x))print(self:, self)classmethoddef class_foo(cls, x):print(调用class_foo函数 (%s, %s) % (cls, x))…

php开发微信图灵机器人

本着开源为原则,为这个世界更美好作出一份共享,我就给大家做个指路人,如果实用,记得给提供开源的朋友一些鼓励。 简单介绍一下实现思路,使用swoole扩展接管php运行,由于swoole只能在类UNIX上运行&#xff0…

jQuery源码的基础知识

序言:DOM addEventListener attachEvent与addEventListener区别适应的浏览器版本不同,同时在使用的过程中要注意attachEvent方法 按钮onclickaddEventListener方法 按钮click一、arguments对象: 1、arguments 属性 为当前执行…

python如何读取数据并输出为表格_Python实现将数据库一键导出为Excel表格的实例...

数据库数据导出为excel表格,也可以说是一个很常用的功能了。毕竟不是任何人都懂数据库操作语句的。 下面先来看看完成的效果吧。 数据源导出结果依赖 由于是Python实现的,所以需要有Python环境的支持 Python2.7.11 我的Python环境是2.7.11。虽然你用的可…

android手机连接无线路由器上网设置,手机连接无线网络怎么设置?手机Wifi无线网设置教程...

随着智能手机无线上网的流行,如今很多家庭都会组建Wifi无线网络,目前组建Wifi网络,大致有两种情况,一种是使用无线路由器,另外一种是将笔记本变身无线无路由器,从而实现智能手机也可以免费Wifi上网&#xf…

通俗易懂了解Vuex

1.前言 在使用Vue进行开发的时候,关于vue组件通信的方式,除了通俗易懂了解Vue组件的通信方式这篇博文谈到三种通信方式,其实vue更提倡我们使用vuex来进行组件间的状态管理以及通信问题。Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。…

netcore 内存限制_.NET Core 和 Serverless 构建飞速发展的架构

(给DotNet加星标,提升.Net技能)英文:samueleresca.net译文:cnblogs.com/Rwing/p/fast-growing译者:Rwing本篇文章的第一部分介绍了有关Serverless计算的基本概念。第二部分展示了如何构建 .NET Core的Lambda函数,其中使…

更多Requests的小技巧以及总结

对于requests的爬虫库,我们已经学到了尾声。 我们在这儿可以挖掘出更多的requests的使用小技巧。 一.cookie对象与字典的转换 在爬取目标cookie的时候,我们可以将cookie信息进行简化处理。 现在做一个简单的代码验证看看,使用百度的cookies&a…

进入Undertow Web服务器

随着Java EE 7的到来以及处理诸如Web Sockets API和HTTP升级(例如EJB over HTTP)之类的高级功能的要求,WildFly开发团队已经做出了重要决定。 在长期致力于JBoss Web服务器(Apache Tomcat的一个分支)之后,新…

centos 重启网卡_CentOS6 网络管理之网卡配置及简单路由设置

CentOS6中关于网络配置的命令有很多,本文将介绍几个平时最长用的几个命令,以及网卡IP地址的配置和简单路由配置。1、经常使用的查看IP地址命令为 ifconfig,不跟参数的情况下默认查看所有已启用的网卡信息,如下图所示:如…

绝地求生 android版支持蓝牙吗,《绝地求生》吃鸡必须要顶配吗?这些配置也能畅玩...

导读《绝地求生》火爆之余,很多人在想要加入这款游戏时,却被游戏传闻中的超高配置要求给吓到了,然后心生退意。事实上,吃鸡的配置要求真的这么高吗?其实并不是!传言1:8G内存不能玩这则传言的说法…

《美团机器学习实践》高清PDF+思维导图+美团算法团队

在美团的搜索、推荐、计算广告、风控、图像处理等领域,相关的人工智能技术得到广泛的应用。《美团机器学习实践》包括通用流程、数据挖掘、搜索和推荐、计算广告、深度学习以及算法工程6大部分内容,全面介绍了美团在多个重要方面对机器学习的应用。通过本…

Java 8中的java.util.Random

Java 8中java.util.Random类的简洁功能之一是对其进行了改进,现在可以返回随机的数字流 。 例如,要生成一个介于0(含)和1(不含)之间的随机双精度数的无限流: Random random new Random(); Do…

appium判断元素是否存在_Python+selenium自动化之判定元素是否存在

在测试过程中,我碰到过这类的问题,使用find_element却找不到某个元素而产生异常,这就需要在操作某个元素之前判定该元素是否存在,而selenium中没有判定元素是否存在的方法,或者判定相同的元素有几个,需要操…

棋盘DP三连——洛谷 P1004 方格取数 洛谷 P1006 传纸条 Codevs 2853 方格游戏

P1004 方格取数 题目描述 设有N $\times N$NN的方格图(N $\le 9$)(N≤9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字00。如下图所示(见样例): A0 0 0 0 0 0 0 0 0 0 13 0 0 6 0 0 0 0 0 0 7 0 0 0 0 0 0 14 …