我对reduce
函数的行为感到困惑。
在第一个例子中,我得到了预期的结果:(1-1 / 2)*(1-1 / 3)= 1/3
>>> reduce(lambda x, y: (1 - 1.0/x) * (1 - 1.0/y), [2,3])
0.33333333333333337
在第二个例子中,我没有得到预期的结果:(1-1 / 2)*(1-1 / 3)*(1-1 / 5)= 0.2666666
>>> reduce(lambda x, y: (1 - 1.0/x) * (1 - 1.0/y), [2,3,5])
-1.5999999999999996
有人可以解释一下我缺少的东西吗?
答案 0 :(得分:11)
您需要的是map
和reduce
:
>>> from functools import reduce
>>> yourlist = [2, 3]
>>> reduce(lambda x, y: x*y, map(lambda x: (1-1/x), yourlist))
0.33333333333333337
>>> yourlist = [2, 3, 5]
>>> reduce(lambda x, y: x*y, map(lambda x: (1-1/x), yourlist))
0.2666666666666667
因为地图会将每个项目转换为(1-1/item)
,然后reduce
会将所有项目相乘。
您可以使用更快operator.mul
代替lambda x, y: x * y
,例如:
>>> import operator
>>> yourlist = [2, 3, 5]
>>> reduce(operator.mul, map(lambda x: (1-1/x), yourlist))
0.2666666666666667
感谢@acushner指出这一点(在评论中)。
在这种情况下,实际上很容易看到什么不起作用,只需使用命名函数并添加一些print
s:
def myfunc(x, y):
print('x = {}'.format(x))
print('y = {}'.format(y))
res = (1 - 1.0/x) * (1 - 1.0/y)
print('res = {}'.format(res))
return res
reduce(myfunc, [2, 3])
# x = 2
# y = 3
# res = 0.33333333333333337
reduce(myfunc, [2, 3, 5])
# x = 2
# y = 3
# res = 0.33333333333333337
# x = 0.33333333333333337
# y = 5
# res = -1.5999999999999996
因此它将最后一个结果用作“下一个”x
值。这就是为什么它适用于长度为2的情况,但对于更多的元素,它根本不适用你想要的公式。
您也可以使用简单的map
循环,而不是使用reduce
和for
。让它们更容易,并且大多数时候它们更易读(在某些情况下它比reduce
更快)。
prod = 1
for item in yourlist:
prod *= 1 - 1 / item
print(prod)
是的,而不是1行,它现在是4行,但很容易理解发生了什么(但可能有一些边缘情况,其行为不像reduce
,例如对于空输入) 。
但我通常更喜欢简单的循环而不是复杂的reduce
- 操作,但始终是YMMV。 :)
答案 1 :(得分:2)
编辑:我批准上面的地图/缩小答案。
要理解原因,请阅读:
递减递归使用2个参数调用列表中每个元素的函数:累加器(最后一次调用的值)和当前元素。
所以你得到:
(1 - 1.0/( (1 - 1.0/2) * (1 - 1.0/3) )) * (1 - 1.0/5)
使用:
reduce(lambda acc, x: (1 - 1.0/acc) * (1 - 1.0/x), [2,3,5])
>>-1.5999999999999996
答案 2 :(得分:2)
要添加为什么您得到-1.5999999999999996
作为结果,为了完整性,我们可以使用https://docs.python.org/2/library/functions.html#reduce作为我们的指南进行计算:
第一次迭代(将2
和3
的前2个迭代器值作为x
和y
):
(1 - 1.0 / 2) * (1 - 1.0 / 3)
变为:
0.5 * 0.6666666666666667
产生:
0.33333333333333337
。
然后我们使用0.33333333333333337
继续我们的下一次迭代,将此结果作为x
,将下一个迭代次数5
作为y
:
因此,我们的第二次迭代将是:
(1 - 1.0 / 0.33333333333333337) * (1 - 1.0/5)
变为:
-1.9999999999999996 * 0.8
产生:
-1.5999999999999996
答案 3 :(得分:-1)
在您的第二个示例中,您有3个输入。你需要:
reduce(lambda x, y: (1 - 1.0/x) * (1 - 1.0/y)* (1 - 1.0/z),...