春节前写了个年终奖避税的程序。但是之前的实现上效率有点儿低,这次的博客就写一写如何优化这个程序好了( 其实是快到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);