投身烈火
Keep Walking

年终奖避税程序优化

春节前写了个年终奖避税的程序。但是之前的实现上效率有点儿低,这次的博客就写一写如何优化这个程序好了(其实是快到deadline才想起来要写博客临时抓的题目……

针对这个年终奖避税的程序,优化的主要方向是降低程序运行的速度。之前的实现,最耗时的是getOneMonthBonusgetTwoMonthBonus中的循环。这是在使用穷举搜索最低纳税点。针对穷举的优化,我了解的比较通用的优化方法,一种是增加缓存,一种是对穷举进行剪枝。

经过观察发现,拆分年终奖不可能把年终奖的50%以上拆到工资里,因为年终奖是用商数来确定交税比例的,确定比例所用的表相当于月薪减去起征点。这样我们就减少了一半的计算量。还有,我们还可以用一个对象来存储计算过的拆分方案,如果遇到相同的情况就直接使用对象里面的结果就可以了。

其实在编写其他程序的时候,这两种也是最简单有效的优化方法。概括的说,就是拿空间换时间和减少计算量……

ok,最后放上优化后的程序,这一期的瞎扯就这么愉快的结束了,咱们下期再见~!\( ̄︶ ̄)/

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
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 = {};
// 获取不拆分年奖交税总额
function getOnlyBonusTax(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;
}
// 获取税率等级
function getTaxNum(money){
for (var i = 0, l = taxQuota.length; i < l; i++) {
if (money <= taxQuota[i]) {
return i;
}
}
return i;
}
// 获取税率
function getTaxRat(money){
return taxRat[getTaxNum(money)];
}
// 获取速算扣除数
function getTaxQuick(money) {
return taxQuick[getTaxNum(money)];
}
// 获取平常月交税金额
function getMonthTax(money) {
if (money > baseQuota) {
return getTax(money - baseQuota);
} else {
return 0;
}
}
// 获取年奖平均月交税金额
function getPerMonthTax(money) {
return getTax(money);
}
// 计算个人所得税金额
function getTax(money) {
return round(money * getTaxRat(money) - getTaxQuick(money), 2);
}
// 计算增加的交税金额
function getMonthTaxAdd(monthSalary, addNum) {
var monthAddSalary = monthSalary + addNum;
monthTaxAdd = round(getMonthTax(monthAddSalary) - getMonthTax(monthSalary), 2);
return monthTaxAdd;
}
// 计算金额小数
function round(num, toFix) {
return parseFloat(num.toFixed(toFix), 10);
}
// 获取拆分为一个月交税总额
function getOneMonthBonus(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;
}
// 获取拆分为两个月交税总额
function getTwoMonthBonus(yearBonus, monthSalary) {
var cached = cacheGetTwoMonthBonus[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
};
cacheGetTwoMonthBonus[yearBonus + ',' + monthSalary] = result
return result
}
function run(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);