考虑下面四个百分比,用浮点数表示:
13.626332%
47.989636%
9.596008%
28.788024%
-----------
100.000000%
我需要用整数表示这些百分比。如果我简单地使用Math.round(),我最终得到的总数是101%。
14 + 48 + 10 + 29 = 101
如果我使用parseInt(),我最终得到了97%。
13 + 47 + 9 + 28 = 97
有什么好的算法可以将任何百分比数表示为整数,同时还保持总数为100%?
编辑:在阅读了一些评论和回答后,显然有很多方法可以解决这个问题。
在我看来,为了保持数字的真实性,“正确”的结果是最小化总体误差的结果,定义为相对于实际值会引入多少误差舍入:
value rounded error decision
----------------------------------------------------
13.626332 14 2.7% round up (14)
47.989636 48 0.0% round up (48)
9.596008 10 4.0% don't round up (9)
28.788024 29 2.7% round up (29)
在平局的情况下(3.33,3.33,3.33)可以做出任意的决定(例如3,4,3)。
下面是一个实现了最大余数方法的Ruby宝石:
https://github.com/jethroo/lare_round
使用方法:
a = Array.new(3){ BigDecimal('0.3334') }
# => [#<BigDecimal:887b6c8,'0.3334E0',9(18)>, #<BigDecimal:887b600,'0.3334E0',9(18)>, #<BigDecimal:887b4c0,'0.3334E0',9(18)>]
a = LareRound.round(a,2)
# => [#<BigDecimal:8867330,'0.34E0',9(36)>, #<BigDecimal:8867290,'0.33E0',9(36)>, #<BigDecimal:88671f0,'0.33E0',9(36)>]
a.reduce(:+).to_f
# => 1.0
不要把四舍五入的数字相加。你会得到不准确的结果。总数可能会显著偏离,这取决于术语的数量和小数部分的分布。
显示四舍五入的数字,但和实际值。根据你呈现数字的方式不同,实际的方法也会有所不同。这样你就能得到
14
48
10
29
__
100
不管怎样,都会有差异。在你的例子中,没有办法显示加起来等于100的数字而不以错误的方式“舍入”一个值(最小的错误是将9.596更改为9)
EDIT
你需要在以下选项中做出选择:
项目的准确性
和的准确性(如果你是四舍五入的值)
四舍五入的项目与四舍五入的总和的一致性)
大多数情况下,当处理百分比时,第三种方法是最好的选择,因为当总数等于101%时比当单个项目的总数不等于100时更明显,并且您可以保持单个项目的准确性。“舍入”9.596到9在我看来是不准确的。
为了解释这一点,我有时会添加一个脚注,解释各个值是四舍五入的,可能不是100% -任何理解四舍五入的人都应该能够理解这个解释。
因为这里没有一个答案似乎能正确解决这个问题,下面是我使用下划线的半模糊版本:
function foo(l, target) {
var off = target - _.reduce(l, function(acc, x) { return acc + Math.round(x) }, 0);
return _.chain(l).
sortBy(function(x) { return Math.round(x) - x }).
map(function(x, i) { return Math.round(x) + (off > i) - (i >= (l.length + off)) }).
value();
}
foo([13.626332, 47.989636, 9.596008, 28.788024], 100) // => [48, 29, 14, 9]
foo([16.666, 16.666, 16.666, 16.666, 16.666, 16.666], 100) // => [17, 17, 17, 17, 16, 16]
foo([33.333, 33.333, 33.333], 100) // => [34, 33, 33]
foo([33.3, 33.3, 33.3, 0.1], 100) // => [34, 33, 33, 0]
下面是一个实现了最大余数方法的Ruby宝石:
https://github.com/jethroo/lare_round
使用方法:
a = Array.new(3){ BigDecimal('0.3334') }
# => [#<BigDecimal:887b6c8,'0.3334E0',9(18)>, #<BigDecimal:887b600,'0.3334E0',9(18)>, #<BigDecimal:887b4c0,'0.3334E0',9(18)>]
a = LareRound.round(a,2)
# => [#<BigDecimal:8867330,'0.34E0',9(36)>, #<BigDecimal:8867290,'0.33E0',9(36)>, #<BigDecimal:88671f0,'0.33E0',9(36)>]
a.reduce(:+).to_f
# => 1.0
可能做到这一点的“最佳”方法(引用是因为“最佳”是一个主观术语)是保持你所处位置的连续(非积分)计数,并四舍五入该值。
然后将其与历史记录一起使用,以确定应该使用什么值。例如,使用您给出的值:
Value CumulValue CumulRounded PrevBaseline Need
--------- ---------- ------------ ------------ ----
0
13.626332 13.626332 14 0 14 ( 14 - 0)
47.989636 61.615968 62 14 48 ( 62 - 14)
9.596008 71.211976 71 62 9 ( 71 - 62)
28.788024 100.000000 100 71 29 (100 - 71)
---
100
在每个阶段,都不需要四舍五入数字本身。相反,将累积值四舍五入,并计算出从上一个基线中达到该值的最佳整数——该基线是前一行的累积值(四舍五入)。
这是可行的,因为您不会在每个阶段都丢失信息,而是更聪明地使用信息。“正确的”四舍五入值在最后一列,你可以看到它们的和是100。
在上面的第三个值中,您可以看到这与盲目舍入每个值之间的区别。虽然9.596008通常会四舍五入到10,但累积的71.211976正确地四舍五入到71 -这意味着只需要9就可以加上之前的基线62。
这也适用于“有问题的”序列,比如三个大约1/3的值,其中一个应该四舍五入:
Value CumulValue CumulRounded PrevBaseline Need
--------- ---------- ------------ ------------ ----
0
33.333333 33.333333 33 0 33 ( 33 - 0)
33.333333 66.666666 67 33 34 ( 67 - 33)
33.333333 99.999999 100 67 33 (100 - 67)
---
100