1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
| """ LG ourantech@163.com 2019-09-04 这是yuange1975的一个兴趣题。just for fun。
需求: @yuange1975 https://weibo.com/2246379231/I5fgWcrFF?from=page_1005052246379231_profile&wvr=6&mod=weibotime&type=comment
最近在微信的微粒贷里借钱了,发现还款还是一个好的算法征解题。 问题,在微信微粒贷里不同时间借款若干笔,现在有一笔资金,需要还款,求通用的还款算法(还哪些笔欠款)?
已知条件有: 1、微粒贷不支持单笔借款部分还款,一笔还款必须还完。如果支持部分还款,那就问题比较简单了。 2、借款总额度还够,微粒贷还可以马上借款出来。 3、一个还款周期里,不收复利,每天按本金按固定利率算利息。 4、现金的收益利率比借款利率低,否者就不去还款了。 5、假定所有的借款下一还款时间都一样。(微粒贷不同借款还款周期都是每月固定的某一天,如果某笔借款到下一个月的还款时间小于一个月,好像这笔借款第一次还款就会到再下一个月)。这个假设简化一点点。 6、微粒贷每笔借款有最高限额4万,这个没什么影响。 7、微粒贷利率每天万2。 8、现金理财收益每天万1。 9、借款、还款本金必须是100的整数倍。
""" import random
LOAN_RATIO = 0.0002 CASH_RATIO = 0.0001
class Repay(object): """ 还款算法: 每需要还一笔就迭代一轮看应该最先还哪一笔 迭代的内容是 计算还款收益,还款收益的主要分为两种情况: 一是如果手上的金额大于该笔待还款金额,则 还款收益=贷款剩余本金利息-待还款金额现金收益; 二是如果手上的金额小于待还款金额,则 还款收益=贷款剩余本金利息-与手上现金和大于待还款金额的满足借款规则的最小贷款金额利息-待还款金额现金收益;且增加一笔贷款。 选择还款收益为正且最大的那一笔贷款最优先还款 如果没有为正的还款收益贷款,则不还款,停止迭代 如果手上没有现金,则停止迭代 如果没有剩余的贷款,则停止迭代 """ def __init__(self, loans, cash): self.loans = loans self.cash = cash self.update_loans()
def plan(self): max_repay_income = self.get_max_repay_income() while self.loans and max_repay_income > 0: self.pay() self.update_loans() max_repay_income = self.get_max_repay_income() return self.loans, self.cash
def update_loans(self): temp = [] for m, n, i, j, k in self.loans: if self.cash >= j: temp.append([m, n, i, j, round(self.repay_income_1(i, j), 3)]) else: min_aount = self.min_loan_amount(j, self.cash) temp.append([m, n, i, j, round(self.repay_income_2(i, j, min_aount), 3)]) self.loans = temp
def get_max_repay_income(self): if self.loans: return max(i[4] for i in self.loans) else: return 0
def pay(self): self.loans = sorted(self.loans, key=lambda x: x[4]) payment = self.loans.pop() print(f"还款前现金金额: {self.cash}\t\t\t偿还贷款: {payment[:-1]}\t\t剩余现金金额:{round(self.cash-payment[-2], 2)}") if self.cash >= payment[-2]: self.cash = round(self.cash - payment[-2], 2) else: min_aount = self.min_loan_amount(payment[-2], self.cash) self.cash = round(self.cash + min_aount - payment[-2], 2) self.loans.append([min_aount, 5, min_aount, min_aount, 0]) print(f"还款时暂时借款:{min_aount}")
@staticmethod def repay_income_1(principal, balance) -> float: """ 当手中现金大于待还款金额时,计算还款收益。 :param principal: 贷款剩余本金 :param balance: 剩余还款总额 :return: """ return principal*LOAN_RATIO - balance*CASH_RATIO
@staticmethod def repay_income_2(principal, balance, min_loan) -> float: """ 当手中现金小于待还款金额时,先借到使手中金额大于待还款金额的最小借款。在综合计算还款收益。 :param principal: 贷款剩余本金 :param balance: 剩余还款总额 :param min_loan: 最小借款金额 :return: """ return (principal-min_loan)*LOAN_RATIO - balance*CASH_RATIO
@staticmethod def min_loan_amount(balance, cash_num): """ 剩余还款金额大于手中现金是,计算满足规则且覆盖还款金额的最小借款金额。 规则是借款金额必须是100 的整数倍 :param balance: 待还款金额 :param cash_num: 手中现金 :return: """ from math import ceil diff = balance - cash_num return 100*ceil(diff/100)
class GenerateData(object): """ 用于生成满足规则的测试数据。 """
def __init__(self, max_cash, loans_num): self.max_cash = max_cash self.loans_num = loans_num
def gen(self): cash = round(random.random()*self.max_cash) loans = [self.get_loan() for _ in range(self.loans_num)]
return cash, loans
@staticmethod def get_loan(): loan = random.randint(5, 400) * 100 months = [5, 10, 20][random.randint(0, 2)] days = random.randint(0, 30*months) days_1 = random.randint(30, 60)
over_months = (days - days_1)//30 + 1
remain_loan = round(loan*(1 - over_months/months), 2) remain_loan_inter = round(remain_loan * (1 + LOAN_RATIO * (days - (30 * (over_months - 1) + days_1))), 2)
return [loan, months, remain_loan, remain_loan_inter, 0]
if __name__ == '__main__': cash_0, loans_0 = GenerateData(100000, 10).gen()
print(f"初始现金情况:{cash_0}") print(f"贷款数据说明: [初始贷款金额, 贷款期数, 剩余贷款本金, 剩余贷款总额]") print(f"初始贷款情况:{[i[:-1] for i in loans_0]}\n\n")
loans_, cash_ = Repay(loans=loans_0, cash=cash_0).plan() print(f"\n\n剩余贷款:{[i[:-1] for i in loans_]}") print(f"剩余现金金额:{cash_}")
|