等额本息年利率反推方法
等额本息年利率反推
- 一、二分法
- 二、数值逼近法
一、二分法
-
思路:
-
- 定义初始月利率上界和下界, 取中间值
-
- 将中间值带入公式计算得出月供, 与实际月供进行比较
-
- 计算月供 < 实际月供 --> 利率偏低, 将中间值定义为新的下界, 重新计算
-
- 计算月供 > 实际月供 --> 利率偏高, 将中间值定义为新的上界, 重新计算
-
- 重复上述计算, 直至 计算月供与实际月供 的误差小于预期精度, 此时的月利率就是最终利率
-
-
代码
public static void main(String[] args) {double loanAmount = 200000;int term = 60;double yearRate = 0.0875;double monthlyPay = calcMonthlyPay(loanAmount, term, yearRate/12);//二分法double calcYearRate = calcYearRateByBinary(loanAmount, term, monthlyPay);log.info("二分法 ---> 计算年利率: {}, 实际年利率: {}", calcYearRate, yearRate); }/*** 等额本息反推年利率: 二分法* 时间复杂度 O(log n)* 思路: 1. 定义初始月利率上界和下界, 取中间值* 2. 将中间值带入公式计算得出月供, 与实际月供进行比较* 3. 计算月供 < 实际月供 --> 利率偏低, 将中间值定义为新的下界, 重新计算* 4. 计算月供 > 实际月供 --> 利率偏高, 将中间值定义为新的上界, 重新计算* 5. 重复上述计算, 直至 计算月供与实际月供 的误差小于预期精度, 此时的月利率就是最终利率* @param loanAmount 贷款金额* @param term 贷款期数* @param monthlyPay 实际月供* @return*/ public static double calcYearRateByBinary(double loanAmount, int term, double monthlyPay){double mrHi = 1; //月利率上界double mrLo = 0; //月利率下界double precision = 0.000000000001; //预期精度int cnt = 200; //最高计算次数, 防止死循环int i = 0;while (i++<cnt){double mid = (mrLo + mrHi) / 2;double calcMonthlyPay = calcMonthlyPay(loanAmount, term, mid);//计算月供double diff = calcMonthlyPay - monthlyPay;if (Math.abs(diff) <= precision) {//返回年利率log.info("二分法计算次数: {}",i);return new BigDecimal(mid * 12).setScale(4, RoundingMode.HALF_UP).doubleValue();}if (diff > 0) {mrHi = mid;} else {mrLo = mid;}}throw new RuntimeException("计算了"+i+"次, 超出计算次数, 请检查计算方法"); }/*** 等额本息计算月供* @param loanAmount 贷款金额* @param term 贷款期数* @param monthlyRate 月利率* @return*/ public static double calcMonthlyPay(double loanAmount, int term, double monthlyRate){//每月还款金额 = 贷款金额 * 月利率 * (1+月利率)^贷款期数 / ((1+月利率)^贷款期数 - 1)double pow = Math.pow(1 + monthlyRate, term);double molecule = loanAmount * monthlyRate * pow; //分子double denominator = pow - 1; //分母//每月还款金额return molecule / denominator; }
二、数值逼近法
-
思路:
-
- 定义初始月利率
-
- 每一步减 0.1(步长), 得出十分位的值
-
- 每一步减 0.01(步长), 得出百分位的值
-
- 每一步减 0.001(步长), 得出千分位的值
-
- 依次减下去, 直至减的值达到预期精度, 此时的月利率就是最终利率
-
-
代码
public static void main(String[] args) {double loanAmount = 200000;int term = 60;double yearRate = 0.0875;double monthlyPay = calcMonthlyPay(loanAmount, term, yearRate/12);//数值逼近法double calcYearRateApprox = calcYearRateByApprox(loanAmount, term, monthlyPay);log.info("数值逼近法 ---> 计算年利率: {}, 实际年利率: {}", calcYearRateApprox, yearRate); }/*** 等额本息反推年利率: 数值逼近法* 时间复杂度 O(n)* 思路: 1. 定义初始月利率* 2. 每一步减 0.1(步长), 得出十分位的值* 3. 每一步减 0.01(步长), 得出百分位的值* 4. 每一步减 0.001(步长), 得出千分位的值* 5. 依次减下去, 直至减的值达到预期精度, 此时的月利率就是最终利率* @param loanAmount 贷款金额* @param term 贷款期数* @param monthlyPay 实际月供* @return*/ public static double calcYearRateByApprox(double loanAmount, int term, double monthlyPay){double rate = 1; //初始月利率double direction = 0.1; //方向, 决定是加 0.1, 还是减 0.1double jd = 0.1; // 加(减)步长 = direction / jddouble precision = 0.000000000001; //预期精度int cnt = 200; //最大计算次数int i = 0;while (i++ < cnt && Math.abs(direction/jd) > precision){double calcMonthlyPay = calcMonthlyPay(loanAmount, term, rate);//计算月供double diff = calcMonthlyPay - monthlyPay;if(diff * direction > 0){direction = -direction;jd*=10;}rate+=(direction/jd);}log.info("数值逼近法计算次数: {}",i);return new BigDecimal(rate * 12).setScale(4, RoundingMode.HALF_UP).doubleValue(); }/*** 等额本息计算月供* @param loanAmount 贷款金额* @param term 贷款期数* @param monthlyRate 月利率* @return*/ public static double calcMonthlyPay(double loanAmount, int term, double monthlyRate){//每月还款金额 = 贷款金额 * 月利率 * (1+月利率)^贷款期数 / ((1+月利率)^贷款期数 - 1)double pow = Math.pow(1 + monthlyRate, term);double molecule = loanAmount * monthlyRate * pow; //分子double denominator = pow - 1; //分母//每月还款金额return molecule / denominator; }