等额本息计算式的推导

其中P是全额, R是月息, N是期数.

对于等额本息, 每个月的还款数额相同, 但是利息是递减的, 第一个月是全额的一个月利息, 第二个月是去掉第一个月本金后剩余金额的一个月利息, 如下:

假定每月还款为x, 总借款金额为m, 月息为r, 各月所还本金可以按如下计算

a1 = x - m*r
a2 = x - (m - a1)*r
a3 = x - (m - a1 - a2)*r
...
an = x - (m - a1 - ... - an-1)*r
an+1 = x  (因为a1 + ... + an=m)

将a1代入a2的表达式, 然后将a1和a2再代入入a3的表达式, 会得到
a3 = (x - mr)*(1+r)2
a4 = (x - mr)*(1+r)3

...
an+1 = (x - mr)*(1+r)n = x
这就得出了等额本息的x的计算公式.

以天计息的等额本息推导

如果计息需要精确到天, 假设总本金为P, 日利率为r, 一共有n期, 每一期还款为x, 其到期后的本金余额为 Pn 如下:
P1 = P - (x - P * d1 * r) = P * (1 + d1 * r) - x
P2 = P1 * (1 + d2 * r) - x
P3 = P2 * (1 + d3 * r) - x
...
Pn-1 = Pn-2 * (1 + dn-1 * r) - x
Pn = Pn-1 * (1 + dn * r) - x = 0

代入则可以得到
(((P * (1 + d1 * r) - x) * (1 + d2 * r) - x) * (1 + d3 * r) - x) ... * (1 + dn * r) = x
通过不断代入dn即可求得x值

Java实现

    /**
     * 按日标准等额本息法计算每月还款额
     *
     * @param principal 本金金额
     * @param rate 年化利率
     * @param dueDates 日期序列
     * @return 与日期序列大小一致, 因为还款序列正好比日期序列少一位, 使用最后一个值存储月均还款额, 其他则为每次的本金还款额
     */
    public static BigDecimal[] calEquatedMonthlyInstallment(BigDecimal principal, BigDecimal rate, List<Date> dueDates) {
        BigDecimal[] output = new BigDecimal[dueDates.size()];
        BigDecimal k = BigDecimal.ZERO , tmp = BigDecimal.ONE;
        for (int i = dueDates.size() - 1; i > 0; i--) {
            int period = TimeUtil.getDaysDiff(dueDates.get(i - 1), dueDates.get(i));
            k = BigDecimal.valueOf(period).divide(BigDecimal.valueOf(365), 8, BigDecimal.ROUND_HALF_UP).multiply(rate).add(BigDecimal.ONE);
            output[i - 1] = k; // 记住每一步的本金换算比例, 用于倒推本金
            tmp = tmp.divide(k, 8, BigDecimal.ROUND_HALF_UP).add(BigDecimal.ONE);
        }
        // 边界: 最后一步的tmp值并无1, 要扣除后再计算
        BigDecimal amount = principal.divide(tmp.subtract(BigDecimal.ONE), 2, BigDecimal.ROUND_HALF_UP);
        output[dueDates.size() - 1] = amount; //月还款额
        BigDecimal p, pnext = BigDecimal.ZERO, total = BigDecimal.ZERO; // total:累计本金的临时变量, pnext后一个月的本金初始为0, p此月的本金.
        for (int i = dueDates.size() - 2; i > 0; i--) {
            p = pnext.add(amount).divide(output[i], 2, BigDecimal.ROUND_HALF_UP);
            output[i] = p.subtract(pnext); // 差就是本月的本金还款额
            total = total.add(output[i]);
            pnext = p;
        }
        output[0] = principal.subtract(total); // 第一个月用差额, 以满足和与本金精确相等
        return output;
    }
原文地址:https://www.cnblogs.com/milton/p/4868301.html