java增函数的单变量求解,最底层码农的不易谁能体会?谁心里苦谁知道啊。

您所在的位置:网站首页 函数公式大总结 java增函数的单变量求解,最底层码农的不易谁能体会?谁心里苦谁知道啊。

java增函数的单变量求解,最底层码农的不易谁能体会?谁心里苦谁知道啊。

2023-03-04 04:11| 来源: 网络整理| 查看: 265

这是一篇本来不应该存在的程序,但是有了世界之大,就有这一段程序,如果你同情我的遭遇,那么进来帮我改进一下,大家一起交流讨论一下吧,我谢谢大家了。

本来我哪有这个业务啊,这个单变量求解可是excel的功能,谁能想到会有领导让人实现这个功能的,你直接调用excel,让他去算不好嘛,偏偏我对象的领导,就想出了这么一个搜肠刮肚,都想不出来的馊主意,还好这个功能使用的场景比较单一,只是用在了一个增函数上,如果连这个函数,是增函数还是减函数,都不清楚的情况下,以我这考不上985的大脑,估计是想不出来了。

废话说完,直接上货,底下是全部源码,不用着急看,可以先看源码底下的解释。

public class GoalSeek { public static void main(String[] args) { double target = 6.875531; double right; double left; double x=Math.random(); if(f(x) < target){ left = x; do{ x*=10; } while(f(x) < target); right = x; } else{ right = x; do{ x/=10; } while(f(x) > target); left = x; } double threshold = findThreshold(right-left); double result = 0; while(left0){ return Math.pow(10,nums[0].length()-1); }else{ double result = Math.pow(0.1,(nums[1].length() - String.valueOf(Long.parseLong(nums[1])).length())); return result == 1?0.1:result; } } private static double f(double x){ return x*x+3; }

因为是一个增函数,所以需要找到两个值,这两个值需要在目标值的两边,类似于下面这个图:

红点是目标点,绿色是right值,黄色是left值,用二分法一点一点逼近目标值。

1.寻找上下界

所以第一步就是要先找到左右这两个点,代码如下所示:

//1.先随机出一个数来 double x=Math.random(); //如果这个数经过函数计算小于目标值,说明这个数是left值,再继续找right值就可以 //寻找的方法就是将这个随机数扩大10倍(扩大几倍都行),一直到这个数经过函数计算之后大于target就可以 if(f(x) < target){ left = x; do{ x*=10; } while(f(x) < target); right = x; } //如果这个数经过函数计算大于目标值,说明这个数是right值,再继续寻找left值 //寻找的方法和上面寻找right相反,将随机数缩小10倍,直到这个数经过函数计算之后小于target就可以 else{ right = x; do{ x/=10; } while(f(x) > target); left = x; }

2.计算阈值

当使用二分法对值进行逼近的时候,left和right值的加减变得尤为重要,是每一步+1,还是+0.1,还是0.01?这个不好确定,所以就有了如下的方法:

private static double findThreshold(double max){ BigDecimal bigDecimal = new BigDecimal(max); String[] nums = bigDecimal.setScale(7, RoundingMode.HALF_EVEN).toPlainString().split("\\."); if(Integer.parseInt(nums[0])>0){ return Math.pow(10,nums[0].length()-1); }else{ double result = Math.pow(0.1,(nums[1].length() - String.valueOf(Long.parseLong(nums[1])).length())); return result == 1?0.1:result; } }

方法入参是right-left所得,这两个数的差值格式,有三种情况:

1.1,这种情况就是小数点两边都有值,这个时候只需要小数点左边的整数值就可以,如果是1.0就得到1,如果是10.0就得到10,如果是100.0就得到100,以此类推。

1.0,这种情况和上述情况一样,只不过这种情况是恰巧得到一个整数,小数点右边没有数

0.1,这种情况就是小数点左边为0,需要找到右边最靠近小数点的非零数,比如0.123得到0.1,0.0123得到0.01,0.00000123得到0.000001.

上述方法是先将小数转换成String格式,通过小数点将字符串变为两个部分,nums[0]是整数部分,nums[1]是小数部分,这地方有个坑就是要防止double变成科学计数法,例如0.00009变成9E-5这种情况,PHP具体怎么变我不知道。

小数点左侧比较好处理,比如左侧是89,则返回10即可,10的1次方

小数点右侧不太好处理,比如0.0076,右边是0076,先将0076转换为Long型变成76,再转换成字符串,用0076的字符串长度减去76的字符串长度 ,得到目标值2,0.1的平方是0.01。这里有一个特殊情况就是0.1最后得到的结果是0,0.1的0次方等于1,所以当这种情况遇到结果是1的时候,返回0.1即可。

3.防止错过答案

有的时候会出现这样一种情况,例如y=x+3,这个函数,如果你的目标值是5.1,即y=5.1是你的目标值,当x的左右值在加减的时候,会让x错过正确的答案,比如正确答案是x=2.1,但是你的左右值随机出来的值是0.1和2.2,计算出来的阈值是1,第一次二分结束的时候会让右边的值编程1.2,直接就错过了2.1这个正确答案,之后再如何算都是徒劳的,甚至进入死循环!

这个时候就是源代码的第三步:

if(temp > target){ right -= threshold; while(f(right)target){ left -= threshold; threshold*=0.1; left += threshold; } }

这里的思想就是right的函数值是一定要大于目标的,因为是增函数,所以如果right的函数值小于目标,说明阈值大了,那么就将right值恢复,然后将阈值变小,再进行计算,一直到得出结果,左侧同理。

总结

过程就是这么一个过程,作为最底层码农的我,不仅要服务于我的领导,还有我领导的领导,还有客户,还有对象的领导,还有对象领导的客户,你们看吧,我苦不苦,啥也不说了,我又双叒叕坏肚子了。最后我对象还和我说,她有新的任务,暂时先不看了,我... ...



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3