我有一个概率列表,如
0.0442857142857143
0.664642857142857
0.291071428571429
我想将它们转换为最接近的百分比,以便百分比总和加起来
这样的事情
0.0442857142857143 - 4 %
0.664642857142857 - 67 %
0.291071428571429 - 29 %
我不能依赖Math.Round来总是给我结果加起来1.最好的方法是什么?
答案 0 :(得分:3)
这是一种可以完成这项工作的方法。
public int[] Round(params decimal[] values)
{
decimal total = values.Sum();
var percents = values.Select(x=> Math.Round(x/total*100)).ToArray();
int totalPercent = perents.Sum();
var diff = 100 - totalPercent;
percents[percents.Lenght - 1] += diff;
return percents;
}
答案 1 :(得分:2)
有趣的答案集。
这里的问题是您在舍入操作中遇到累积错误。在某些情况下,累积的错误会被取消 - 某些值向上舍入,其他值向下,取消总错误。在其他情况下,例如你在这里的情况,舍入误差都是负数,累计总误差为(大约)-1。
在一般情况下解决此的唯一方法是跟踪总累积错误,并在错误变得足够大时添加/减去。这很乏味,但这是唯一真正做到这一点的方法:
static int[] ToIntPercents(double[] values)
{
int[] results = new int[values.Length];
double error = 0;
for (int i = 0; i < values.Length; i++)
{
double val = values[i] * 100;
int percent = (int)Math.Round(val + error);
error += val - percent;
if (Math.Abs(error) >= 0.5)
{
int sign = Math.Sign(error);
percent += sign;
error -= sign;
}
results[i] = percent;
}
return results;
}
此代码为任何大小的数组生成合理的结果,总和大约+1.0000(或足够接近)。数组可以包含负值和正值,只要总和足够接近+1.0000就不会引入严重错误。
代码累积舍入误差,当总误差超过-0.5 < error < +0.5
的可接受范围时,它会调整输出。使用此方法,您的数字的输出数组将为:[4, 67, 29]
。您可以将可接受的误差范围更改为0 <= error < 1
,给出输出[4, 66, 30]
,但是当数组包含负数时,这会导致奇怪的结果。如果这是您的偏好,请将方法中间的if
语句更改为:
if (error < 0 || error >= 1)
答案 2 :(得分:1)
你可以将数字乘以100(如果你有十进制数)
0.0442857142857143 * 100 = 4 %
0.664642857142857 * 100 = 66 %
0.291071428571429 * 100 = 29 %
E:正确,0.291071428571429不会加起来达到30%......
答案 3 :(得分:0)
由于您似乎并不关心碰到哪个号码,我会使用最后一个号码。算法非常简单,并且适用于.4边缘情况,你必须添加1和.5,你必须删除1:
1)围绕每个数字,但最后一个 2)从你的总和中减去100 3)将余数分配给最后一个数字
作为扩展方法,它看起来像这样:
public static int[] SplitIntoPercentage(this double[] input)
{
int[] results = new int[input.Length];
for (int i = 0; i < input.Length - 1; i++)
{
results[i] = (int)Math.Round(input[i] * 100, MidpointRounding.AwayFromZero);
}
results[input.Length - 1] = 100 - results.Sum();
return results;
}
以下是相关的单元测试:
[TestMethod]
public void IfSumIsUnder100ItShouldBeBumpedToIt()
{
double[] input = new []
{
0.044,
0.664,
0.294
};
var result = input.SplitIntoPercentage();
Assert.AreEqual(100, result.Sum());
Assert.AreEqual(4, result[0]);
Assert.AreEqual(66, result[1]);
Assert.AreEqual(30, result[2]);
}
[TestMethod]
public void IfSumIsOver100ItShouldBeReducedToIt()
{
double[] input = new[]
{
0.045,
0.665,
0.295
};
var result = input.SplitIntoPercentage();
Assert.AreEqual(100, result.Sum());
Assert.AreEqual(5, result[0]);
Assert.AreEqual(67, result[1]);
Assert.AreEqual(28, result[2]);
}
一旦重构,结果如下:
public static int[] SplitIntoPercentage(this double[] input)
{
int[] results = RoundEachValueButTheLast(input);
results = SetTheLastValueAsTheRemainder(input, results);
return results;
}
private static int[] RoundEachValueButTheLast(double[] input)
{
int[] results = new int[input.Length];
for (int i = 0; i < input.Length - 1; i++)
{
results[i] = (int)Math.Round(input[i]*100, MidpointRounding.AwayFromZero);
}
return results;
}
private static int[] SetTheLastValueAsTheRemainder(double[] input, int[] results)
{
results[input.Length - 1] = 100 - results.Sum();
return results;
}
答案 4 :(得分:0)
逻辑是,首先我们必须舍入“十进制后的值”,然后将舍入值应用于整数值。
static long PercentageOut(double value)
{
value = value * 100;
value = Math.Round(value, 1, MidpointRounding.AwayFromZero); // Rounds "up"
value = Math.Round(value, 0, MidpointRounding.AwayFromZero); // Rounds to even
return Convert.ToInt64(value);
}
static void Main(string[] args)
{
double d1 = 0.0442857142857143;
double d2 = 0.664642857142857;
double d3 = 0.291071428571429;
long l1 = PercentageOut(d1);
long l2 = PercentageOut(d2);
long l3 = PercentageOut(d3);
Console.WriteLine(l1);
Console.WriteLine(l2);
Console.WriteLine(l3);
}
<强>输出强>
4
67
29
---
sum is 100 %