如何舍入一组总和为100%的数字

问题描述 投票:3回答:3

今天我的一个朋友在网站上向我展示了一个错误。 (Link)

您可以看到百分比的总和为100.1%。

49 + 20.7 + 10.9 + 7 + 5.5 + 7 = 100.1%

我想:如果我编程并且有7个(浮点数)数字,我怎么能解决这个问题呢?


假设所有数字都有一个小数位:

49.0 + 20.7 + 10.9 + 7.0 + 5.5 + 7.0 = 100.1%

我相信这是一个四舍五入的问题,我没有看到错误的其他解释。

e.g:

49.5%+ 50.5%= 100%,如果我们四舍五入,50%+ 51%= 101%。

但在这种情况下,因为我们有两个数字,我们可以使用该轮甚至。

49.5%+ 50.5%= 100%,如果我们舍入到均匀,50%+ 50%= 100%。

这些数字已经被污染了,因为它们的总和是100.1%,因此,至少有一个数字等于自己减去0.5。

舍入到偶数在这7个数字的例子中不起作用,因为它不会在以下总和中:

49 + 20.65 + 10.85 + 7 + 5.5 + 7 = 100%

49 + 20.6 + 10.8 + 7 + 5.5 + 7 = 99.9%

有没有简单/快捷的方法来解决这个问题?使用不同的舍入方法?

或者我能解决这个问题的唯一方法是创建一个函数来专门处理我的问题?


注意:我不是在寻找特定的编程语言方法。我希望我能用不同的语言解决这个问题(例如在PHP和C ++中)。

rounding
3个回答
3
投票

首先,您对可能原因的分析不是这里的实际原因。但是你所谈论的概念在舍入方案中称为偏差。它确实存在并且是真实的 - 它不仅仅是您在此处的具体问题的原因。

在您声明的示例中导致:49.5%+ 50.5%= 100%,向上舍入,50%+ 51%= 101%。向上舍入(向正向无穷大舍入)相当于正数向左舍入为零。请参阅1下面的舍入方案列表。但同样,这不太可能成为原因,除非您碰巧得到两个相同的数字,它们相同等于a.5和b.5不是a.7 + b.8 + c.5,或任何其他组合数字。为了证明为什么这不是真正的原因,在这个n个数字的列表中,有(n-1)个相邻对,如果我们做出合理的假设,即每个最后一个数字是同等可能的,那么获得相邻数字a的机会。 5,b.5仅为(0.1)^ 2 = 0.01

无论如何,这里的真正原因是缺失精度引入的数值误差(由于转换为字符串'%2.1f'的数字的截断表示)(无论使用哪种语言,大概是PHP,Javascript或Java)......

通常和最简单的解决方案是提供更高的精度。严格来说,你可能只需要一个(或两个)数字,但IEEE 754 floats give you 23 digits of mantissa for free, so everyone uses that

但是,如果你真的坚持要为自己设置一个精确缺失的舍入数字的(人为)挑战,并且在约束条件下它们必须总和为100.0%(或最大化它们的机会),那么有几种较少使用的舍入方案。您可以在教科书中找到它们,并且由于显而易见的原因它们在现实世界中使用不多,因为它们会引入随机性和可能的​​非确定性(尽管您可以设置随机种子,至少确保可重复性)。

因此,无论它在哪里值得的是那些舍入方案(以及许多其他方案,请参阅整篇文章):

[2] http://en.wikipedia.org/wiki/Rounding#Tie-breaking

以下都会导致q = .5情况的偏差,并且你说你想要避免使用它们(而不是携带额外的精度,这会使问题消失):

  • 圆的一半
  • 圆半下来
  • 离零的一半
  • 圆半朝零
  • 圆一半甚至
  • 圆半到奇数

现在,您有兴趣:

  • 随机舍入:另一种无偏见的打破平局方法是随机舍入:

如果y的小数部分是.5,则在y + 0.5和y - 0.5之间随机选择q,概率相等。优点:基本上没有整体偏差;但它在偶数和奇数q值中也是“公平的”。另一方面,它在结果中引入了随机分量;对相同数据执行两次相同的计算可能产生两种不同的结果。此外,如果人类(而不是计算机或偶然机器)“随机”决定在哪个方向上进行回合,那么它对无意识偏见持开放态度。

  • 交替打破平局:一种方法,比大多数方法更模糊,是交替的半圆。

如果小数部分是0.5,则交替向上舍入和向下舍入:对于第一次出现的0.5小数部分,向上舍入;第二次出现,向下舍入;等等。如果可以有效地编号出现0.5个小数部分,则这抑制了结果的随机分量。但是,如果出现的总数是奇数,它仍然可以根据分配给第一次出现的舍入方向引入正偏差或负偏差。

如果你想阅读所有这些东西(计算机算术和实现它的硬件电路),一个很好的参考(在硬件方面很重)是

计算机算术算法,第2版,以色列Koren www.ecs.umass.edu/ece/koren/arith/马萨诸塞大学阿默斯特分校,2010年


1
投票

您不应累积舍入值,而应使用(几乎)精确值。

解决它的另一种肮脏方式可能是:

if (sum > 100){
    sum = 100;
}

另一种方法是通过两个和的差来校正值,如注释中所述(算法的实现在JavaScript中,快速和脏的仅用于演示目的,并且仅在舍入到完整整数时有效,否则需要修改为位):

var numbers = [49, 20.7, 10.8, 7, 5.5, 7];
var roundedNumbers = [49, 21, 11, 7, 6, 7];

var sum = numbers.sum();
var roundedSum = roundedNumbers.sum();

while (roundedSum != sum){
    var isRoundeSumLarger = roundedSum > sum;
    var maxDifferenceIndex;
    var maxDifferenceValue = 0;
    for (var n = 0; n < numbers.length; n++){
       var difference = Math.abs(roundedNumbers[n] - numbers[n]);
       if ((isRoundeSumLarger && roundedNumbers[n] > numbers[n] && maxDifferenceValue < difference)
         ||(!isRoundeSumLarger && roundedNumbers[n] < numbers[n] && maxDifferenceValue < difference)){
           maxDifferenceValue = difference;
           maxDifferenceIndex = n;
       }
    }
    var modifyValue = (isRoundeSumLarger ? -1 : 1); // or whatever granularity the rounding algorithm is
    roundedNumbers[maxDifferenceIndex] += modifyValue;
    roundedSum += modifyValue;
}
console.log(numbers, sum); // [49, 20.7, 10.8, 7, 5.5, 7] 100
console.log(roundedNumbers, roundedSum); // [49, 21, 11, 7, 5, 7] 100

0
投票

在计算百分比时会固有地引入“错误”,并通过向下/向上舍入来降低精度。当你总结那些四舍五入的数字时,你将获得不同的总数。

我认为你所建议的是捏造四舍五入的数字,以便他们总结一个“原始”总数,你可以像这样编程,例如:

1. sum the percentages = 100.1
2. calculate error: 100 - 100.1 = -0.1
3. select any one of the percentages and negate the error.
4. sum them again, will equal 100.

当然,这意味着修改后的百分比不再反映原始数据(即使是通过小数量),也没有正确的方法来做到这一点。

现实情况是原始数学没有“问题”,只有语义。我倾向于指定数字是四舍五入的,或者显示完整的小数。

© www.soinside.com 2019 - 2024. All rights reserved.