这不是关于如何计算Python中的平均值的问题,而是在比较两个数字列表的平均值时如何平衡精度和速度的问题。
此问题以学生的成绩为框架,因此要比较的“典型”输入类似于[98, 34, 80]
和[87, 65, 90, 87]
。然而,我遇到了一些明显涉及非常大数字的测试用例,因为我偶尔会OverflowError
return float(average)
。
有以下测试用例,使用float()
返回错误答案:
x = [9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999,
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999,
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999]
y = [9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999,
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999,
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999998]
x
和y
的平均值非常接近,但不相等。从我所看到的,获得正确答案的唯一方法是使用Decimal
或Fraction
,但这些速度较慢。
这是一个快速的性能分析。
def mean_fractions(nums):
return Fraction(sum(nums), max(len(nums), 1))
def mean_builtins(nums):
return sum(nums) / float(max(len(nums), 1))
def mean_decimal(nums):
return Decimal(sum(nums)) / max(len(nums), 1)
# test runner
@timeit
def do_itt(func, input, times):
for i in range(times):
func(input)
do_ittt(mean_builtins, y, 1000000) # took: 0.9550 sec
do_ittt(mean_decimal, y, 1000000) # took: 3.0867 sec
do_ittt(mean_fractions, y, 1000000) # took: 3.2718 sec
do_ittt(mean_builtins, [96, 43, 88], 1000000) # took: 0.7679 sec
do_ittt(mean_decimal, [96, 43, 88], 1000000) # took: 1.4871 sec
do_ittt(mean_fractions, [96, 43, 88], 1000000) # took: 2.6341 sec
我们可以看到使用内置提供了显着的加速,甚至忽略了如果您希望最终结果为float
,则需要转换Decimal
和Fraction
对象。
问题
所以我的问题是,鉴于这些速度差异,有一个很好的方法来了解builtins
方法何时适用于某些列表a
和b
,以及何时它会提供错误的答案吗?在上面的x
和y
上,它说它们是相同的,这是错误的,但在[96, 43, 88]
和[87, 50]
它可以正常工作精细。
答案 0 :(得分:2)
假设原始分数始终为整数。 Python float
是64位IEEE 754浮点数。这可以表示基数10中15位或更少位数的任何整数,或more precisely,它可以表示任何最大为9,007,199,254,740,993的整数。
因此,如果您的得分总和超过此值,则可能会以您概述的方式使用float
时出现问题。
如果像Stefan Pochmann在下面的评论中指出的那样,你有大笔金额但不是那么大,那么你也可能会遇到问题:
6755399441055745.0 / 3 == 6755399441055746.0 / 3 # True
因此,您需要保持在划分结果的15位有效数字的限制之下。如果将15位数字除以3,则“丢失”一位数,因为除法可能不会使整数部分具有更少的位数,并且它需要小数部分的额外数字。这可能意味着单个“备用”有效数字就足够了,但即便这样也不够(我还没有测试过)。但当然,如果得分总和为1千万亿或更高,你会想要使用更高精度的类型。