考虑下面四个百分比,用浮点数表示:

    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)。


当前回答

或者像这样简单,你只需要累积误差…

const p = [13.626332, 47.989636, 9.596008, 28.788024];
const round = (a, e = 0) => a.map(x => (r = Math.round(x + e), e += x - r, r));
console.log(round(p));

结果:[14,48,9,29]

其他回答

下面是@varun-vohra答案的一个简单的Python实现:

def apportion_pcts(pcts, total):
    proportions = [total * (pct / 100) for pct in pcts]
    apportions = [math.floor(p) for p in proportions]
    remainder = total - sum(apportions)
    remainders = [(i, p - math.floor(p)) for (i, p) in enumerate(proportions)]
    remainders.sort(key=operator.itemgetter(1), reverse=True)
    for (i, _) in itertools.cycle(remainders):
        if remainder == 0:
            break
        else:
            apportions[i] += 1
            remainder -= 1
    return apportions

你需要math, itertools, operator。

检查如果这是有效的或不就我的测试用例,我能够得到这个工作。

假设number是k;

按降序排序百分比。 从降序遍历每个百分比。 计算k的百分比第一个百分比采取数学。输出的天花板。 下一个k = k-1 遍历直到所有百分比被消耗。

因为这里没有一个答案似乎能正确解决这个问题,下面是我使用下划线的半模糊版本:

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]

如果是四舍五入,就没有办法在所有情况下都得到完全相同的结果。

你可以取你拥有的N个百分比的小数部分(在你给出的例子中是4)。

把小数部分相加。在你的例子中,总分为3。

将分数最高的3个数字上排,其余的取底。

(抱歉修改了)

只要您不关心对原始十进制数据的依赖,就有许多方法可以做到这一点。

第一种也是最流行的方法是最大余数法

基本上就是:

四舍五入 求sum和100的差值 将差值按小数部分的递减顺序加1

在你的例子中,它是这样的:

13.626332%
47.989636%
 9.596008%
28.788024%

如果取整数部分,就得到

13
47
 9
28

加起来是97,再加3。现在,你看小数点部分

.626332%
.989636%
.596008%
.788024%

取最大的,直到总数达到100。所以你会得到:

14
48
 9
29

或者,您可以简单地选择显示一个小数位而不是整数值。所以数字是48.3和23.9等等。这会使方差从100下降很多。