【问题标题】:Python reduce functionPython减少函数
【发布时间】:2018-12-01 09:30:04
【问题描述】:

我只是在学习 Python,不了解我从 reduce 函数中得到的行为。我见过很多例子,当你想乘法时,你可以使用 reduce 来执行等效的求和函数:

f = [2,3,4]
reduce(lambda x,y: x*y,f)

这给了我期望的价值。但我需要乘以所有倒数。我想我可以这样做:

reduce(lambda x,y: 1/x * 1/y, f)

但结果是 1.5 而不是一些小得多的十进制答案。我做错了什么?

【问题讨论】:

  • 您是否尝试过在计算中使用括号:reduce(lambda x,y: (1/x) * (1/y), f)?
  • @LukasBach:碰巧,在这种情况下,括号(大部分)是不相关的(主要是因为运算顺序会稍微改变浮点数学的结果)。即使没有括号,它也会变成((1 / x) * 1) / y,这在逻辑上是等价的。问题在x的意思。
  • “取倒数”部分更适合map,而不是将其折叠到reduce:reduce(lambda x, y: x*y, map(lambda x: 1/x, f))。
  • (当然,使用reduce、map或lambda在Python中并不像在函数式语言中那样受到鼓励,并且将所有倒数相乘是相当于将所有数字相乘并在最后取倒数一次,这会更容易。)

标签: python lambda reduce


【解决方案1】:

每次调用的x 是最后一次调用的结果(它只是第一次调用的直接输入之一),因此每次执行1 / x 都会取上一个结果的倒数。要解决此问题,您需要将lambda 更改为仅乘以新数字的倒数,而不是累加值。您还需要提供一个初始中性值 (1),以便 f 中的第一个值正确取其倒数(否则,它将是 f[0] 的普通值乘以 f[1:] 的倒数) :

# x is accumulated product of reciprocals to date, *DON'T* take reciprocal again
reduce(lambda x, y: x * (1 / y), f, 1)
                                  # ^ multiplicative identity is correct neutral value here

也就是说,您可以再简化一点; x * (1 / y) (大致考虑到浮点精度问题)等同于 x / y,因此您可以进一步简化为:

reduce(lambda x, y: x / y, f, 1)

或使用the operator module 将所有工作推到C 层(仅当f 可能非常大时才重要):

import operator
reduce(operator.truediv, f, 1)

无论哪种方式,这都会得到预期的结果:

>>> (1/2) * (1/3) * (1/4)
0.041666666666666664
>>> reduce(lambda x,y: x * (1 / y), f, 1)
0.041666666666666664
>>> reduce(lambda x,y: x / y, f, 1)
0.041666666666666664
>>> reduce(operator.truediv, f, 1)
0.041666666666666664

如下面的 cmets 所述,计算单个倒数并将它们全部相乘比仅计算 f 中所有值的乘积更慢且更容易出错(尤其是当所有输入均为 int 时),然后在最后计算该产品的倒数一次。在 Python 3.8+ 上,使用 math.prod,这很简单:

>>> 1 / math.prod(f)
0.041666666666666664

在旧版本的 Python 上,您必须创建自己的乘积计算函数,但使用 reduce+operator.mul 很容易做到:

>>> 1 / reduce(operator.mul, f)
0.041666666666666664

【讨论】:

  • 为什么不把所有的数字相乘,然后把倒数作为最后一步呢?应该有更好的准确性。
  • Tangent:如果f 很大,您最终可能会遇到浮点不精确的问题。避免这种情况的一个技巧是将操作数转换为fractions.Fraction;如果您的所有操作数都是整数(无损),Fraction 形式也是如此,因此您可以获得Fraction 的结果而不会损失精度,或者仅在最后转换为float,从而限制精度单次操作的损失。 reduce(truediv, map(Fraction, f), 1) 产生 Fraction(1, 24),float(reduce(truediv, map(Fraction, f), 1)) 产生 0.041666666666666664,只执行了一次舍入。
  • @kindall:嘿,好点子!我正在输入具有相同最终结果的评论,但是是的,坚持使用纯整数会更快。了解Fractions 方法可能对其他更复杂的场景有用,在这些场景中Fractions 的无损方面很有用,但在这种情况下,1 / reduce(operator.mul, f) 几乎肯定是最快、最简单的解决方案。
  • 谢谢ShadowRanger先生。我完全明白如何使用 Reduce!
猜你喜欢
  • 1970-01-01
  • 2020-04-03
  • 2023-03-24
  • 2018-04-16
  • 2014-08-04
  • 1970-01-01
  • 2016-01-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多