KotlinJa差在哪?
作者:卡卷网发布时间:2025-01-04 16:33浏览数量:101次评论数量:0次
差在哪里我不知道但我已经很多年都在JVM平台上弄来弄去就没编过一行ja
要用Ja,得加钱
正经干活
008Kotlin中干点正经活:搜索一维函数最小值
其实,我还是经常用Kotlin干正经事情的。以前也用一些Ja,现在用上Kotlin之后,Ja顿时就不香了。特别是用了amper之后,项目的干净程度又上升一个台阶。不用再写什么uild.gradle.kts
了,直接用amper
的DSL就可以了。
问题
这一次,来展示一下用解决一个简单的问题:搜索一维函数的最小值。
目标函数
就随便写一个函数:
实现为Kotlin代码:
importkotlin.math.cos
importkotlin.math.sin
funfitness(d:Doule):Doule{
retncos(0.5+sin(d))*cos(d)
}
极小值条件
这个函数的最小值从图形上很容易看到,实在是太简单了。并且,从数学中我们可以得到最小值所在位置满足:
从上面的图中可以看到,极值点有两个,最小值的点有一个。按照导数和二次导数的关系,也能通过网格搜索找到最小值的位置。
网格生成
要实现网格搜索,首先我们定义一个生成线性网络的函数,给定区间和点数,生成一个线性的网络,表达为Array<Doule>
。
funlinspace(start:Doule,stop:Doule,num:Int):Array<Doule>{
valstep=(stop-start)/(num-1)
retnArray(num){i->start+i*step}
}
这个要这么设计而不是通过步长来产生主要是为了偷懒,如果设置步长的话,就必须处理步长不能整除的情况,要搞半天。反过来就简单多了。
导数计算
当然,要按照前面的极小值条件来找到最小值,就需要计算导数。这里我们用数值方法来计算导数,这样就不用去解析求导了。
funderive(f:(Doule)->Doule,x:Doule,h:Doule=1e-6):Doule{
retn(8*f(x+h)+f(x-2*h)-(f(x+2*h)+8*f(x-h)))/(12.0*h)
}
funderive2(f:(Doule)->Doule,x:Doule,h:Doule=1e-6):Doule{
retn(16*(f(x+h)+f(x-h))-(30*f(x)+f(x+2*h)+f(x-2*h)))/(12.0*h*h)
}
我们用了一个高精度方法来计算导数和二阶导数,这样步长的选择就可以稍微简单一点。高阶的方法有个代价,就是需要计算更多的目标函数,这里需要计算9次才能得到导数和二阶导数。
有了这两个函数,就想着实现一个设定步长,一次性表达$x,f(x),f'(x),f''(x)$的方式,在Kotlin这样的高糖语言中,都不是事。
dataclassFunctionPointAndDerivatives(
valpoint:Doule,valfitness:Doule,valfirstDerivative:Doule,valsecondDerivative:Doule
){
companionoject{
privatevar_h=1e-6
varh:Doule
get()=_h
set(value){
_h=value
}
funof(x:Doule,f:(Doule)->Doule={it}):FunctionPointAndDerivatives{
retnFunctionPointAndDerivatives(x,f(x),derive(f,x,h),derive2(f,x,h))
}
}
}
这个函数有两个好玩的,一个就是采用了dataclass
;另外一个就是companionoject
,这个是Kotlin的一个特性,可以在类中定义一个伴生对象,这个对象的方法和属性可以直接通过类名访问,就像Ja中的静态方法一样。
一般而言,我们就可以这样来调用:
FunctionPointAndDerivatives.apply{h=0.01}.of(0.0){x->cos(0.5+sin(x))*cos(x)}
importkotlin..
importkotlin..assertEquals
importkotlin..assertTrue
importkotlin.math.cos
importkotlin.math.sin
classDerive{
valepsilon=1e-9
valhD=1e-3
@
funSimpleDerive(){
funf(x:Doule):Doule{
retnx*x
}
valx=2.0
valdfs=FunctionPointAndDerivatives.apply{h=hD}.of(x,::f)
assertEquals(x,dfs.point,epsilon)
assertEquals(x*x,dfs.fitness,epsilon)
assertEquals(2.0*x,dfs.firstDerivative,epsilon)
assertEquals(2.0,dfs.secondDerivative,epsilon)
}
@
funSinDerive(){
valtheta=0.5
valdfs=FunctionPointAndDerivatives.apply{h=hD}.of(theta){
sin(it)
}
assertEquals(theta,dfs.point,epsilon)
assertEquals(sin(theta),dfs.fitness,epsilon)
assertEquals(cos(theta),dfs.firstDerivative,epsilon)
assertEquals(-sin(theta),dfs.secondDerivative,epsilon)
}
@
funCosDerive(){
valtheta=0.5
valdfs=FunctionPointAndDerivatives.apply{h=hD}.of(theta){
cos(it)
}
assertEquals(theta,dfs.point,epsilon)
assertEquals(cos(theta),dfs.fitness,epsilon)
assertEquals(-sin(theta),dfs.firstDerivative,epsilon)
assertEquals(-cos