春节前写了个年终奖避税的程序。但是之前的实现上效率有点儿低,这次的博客就写一写如何优化这个程序好了( 其实是快到deadline才想起来要写博客临时抓的题目……
)
针对这个年终奖避税的程序,优化的主要方向是降低程序运行的速度。之前的实现,最耗时的是 getOneMonthBonus
和 getTwoMonthBonus
中的循环。这是在使用穷举搜索最低纳税点。针对穷举的优化,我了解的比较通用的优化方法,一种是增加缓存,一种是对穷举进行剪枝。
经过观察发现,拆分年终奖不可能把年终奖的50%以上拆到工资里,因为年终奖是用商数来确定交税比例的,确定比例所用的表相当于月薪减去起征点。这样我们就减少了一半的计算量。还有,我们还可以用一个对象来存储计算过的拆分方案,如果遇到相同的情况就直接使用对象里面的结果就可以了。
其实在编写其他程序的时候,这两种也是最简单有效的优化方法。概括的说,就是拿空间换时间和减少计算量……
ok,最后放上优化后的程序,这一期的瞎扯就这么愉快的结束了,咱们下期再见~!/( ̄︶ ̄)/
var baseQuota = 3500; var taxQuota = [1500, 4500, 9000, 35000, 55000, 80000]; var taxRat = [0.03, 0.10, 0.20, 0.25, 0.30, 0.35, 0.45]; var taxQuick = [0, 105, 555, 1005, 2755, 5505, 13505]; var cacheGetOnlyBonusTax = {}; var cacheGetOneMonthBonus = {}; var cacheGetTwoMonthBonus = {}; // 获取不拆分年奖交税总额 functiongetOnlyBonusTax(yearBonus, monthSalary){ var cached = cacheGetOnlyBonusTax[yearBonus + ',' + monthSalary] if (typeof cached != 'undefined') { return cached; } var tax = 0; if (monthSalary > baseQuota) { perMonth = yearBonus / 12; tax = round(yearBonus * getTaxRat(perMonth) - getTaxQuick(perMonth), 2); } else { if (yearBonus < baseQuota - monthSalary) { tax = 0; } else { perMonth = (yearBonus - (baseQuota - monthSalary)) / 10; tax = (yearBonus - (baseQuota - monthSalary)) * getTaxRat(perMonth) - getTaxQuick(perMonth); } } cacheGetOnlyBonusTax[yearBonus + ',' + monthSalary] = tax return tax; } // 获取税率等级 functiongetTaxNum(money){ for (var i = 0, l = taxQuota.length; i < l; i++) { if (money <= taxQuota[i]) { return i; } } return i; } // 获取税率 functiongetTaxRat(money){ return taxRat[getTaxNum(money)]; } // 获取速算扣除数 functiongetTaxQuick(money){ return taxQuick[getTaxNum(money)]; } // 获取平常月交税金额 functiongetMonthTax(money){ if (money > baseQuota) { return getTax(money - baseQuota); } else { return 0; } } // 获取年奖平均月交税金额 functiongetPerMonthTax(money){ return getTax(money); } // 计算个人所得税金额 functiongetTax(money){ return round(money * getTaxRat(money) - getTaxQuick(money), 2); } // 计算增加的交税金额 functiongetMonthTaxAdd(monthSalary, addNum){ var monthAddSalary = monthSalary + addNum; monthTaxAdd = round(getMonthTax(monthAddSalary) - getMonthTax(monthSalary), 2); return monthTaxAdd; } // 计算金额小数 functionround(num, toFix){ return parseFloat(num.toFixed(toFix), 10); } // 获取拆分为一个月交税总额 functiongetOneMonthBonus(yearBonus, monthSalary){ var cached = cacheGetOneMonthBonus[yearBonus + ',' + monthSalary] if (typeof cached != 'undefined') { return cached } var nowBonusTax = getOnlyBonusTax(yearBonus, monthSalary); var halfBonus = round(yearBonus/2); for (var i = 1; i < halfBonus; i++) { var bonusRemain = yearBonus - i; var monthTaxAdd = getMonthTaxAdd(monthSalary, i); var bonusTax = round(getOnlyBonusTax(bonusRemain, monthSalary) + monthTaxAdd, 2); if (bonusTax < nowBonusTax) { nowBonusTax = bonusTax; oneMonth = i; } } var result = { tax: nowBonusTax, bonus: i }; cacheGetOneMonthBonus[yearBonus + ',' + monthSalary] = result; return result; } // 获取拆分为两个月交税总额 functiongetTwoMonthBonus(yearBonus, monthSalary){ var cached = cacheGetTowMonthBonus[yearBonus + ',' + monthSalary] if (typeof cached != 'undefined') { return cached } var nowBonusTax = getOnlyBonusTax(yearBonus, monthSalary); var halfBonus = round(yearBonus/2); for (var i = 1; i < halfBonus; i++) { var bonusRemain = yearBonus - i; var monthTaxAdd = getMonthTaxAdd(monthSalary, (i / 2)) * 2; var bonusTax = round(getOnlyBonusTax(bonusRemain, monthSalary) + monthTaxAdd, 2); if (bonusTax < nowBonusTax) { nowBonusTax = bonusTax; twoMonth = i / 2; } } var result = { tax: nowBonusTax, bonus: i }; cacheGetTowMonthBonus[yearBonus + ',' + monthSalary] = result return result } functionrun(yearBonus, monthSalary){ var startTime = (new Date).getTime(); var yearBonusTax = getOnlyBonusTax(yearBonus, monthSalary); var oneMonthTax = getOneMonthBonus(yearBonus, monthSalary); var twoMonthTax = getTwoMonthBonus(yearBonus, monthSalary); var minTax = Math.min(yearBonusTax, oneMonthTax.tax, twoMonthTax.tax); var bonusRemain = yearBonus - minTax; if (minTax == yearBonusTax) { console.log('年终奖发放:' + yearBonus, '第一个月发放:' + 0, '第二个月发放:' + 0, '实际收入:' + bonusRemain); } else if (minTax == oneMonthTax.tax) { console.log('年终奖发放:' + (yearBonus - oneMonthTax.bonus), '第一个月发放:' + oneMonthTax.bonus, '第二个月发放:' + 0, '实际收入:' + bonusRemain); } else if (minTax == twoMonthTax.tax) { console.log('年终奖发放:' + (yearBonus - (twoMonthTax.bonus * 2)), '第一个月发放:' + twoMonthTax.bonus, '第一个月发放:' + twoMonthTax.bonus, '实际收入:' + bonusRemain); } var endTime = (new Date).getTime(); console.log('耗时:' + (endTime - startTime) + 'ms'); } run(19000, 9500);