python列表乘积_关于python:返回列表的乘积

有没有更简洁,有效或简单的pythonic方法来执行以下操作?

1

2

3

4

5def product(list):

p = 1

for i in list:

p *= i

return p

编辑:

我实际上发现这比使用operator.mul快一点:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32from operator import mul

# from functools import reduce # python3 compatibility

def with_lambda(list):

reduce(lambda x, y: x * y, list)

def without_lambda(list):

reduce(mul, list)

def forloop(list):

r = 1

for x in list:

r *= x

return r

import timeit

a = range(50)

b = range(1,50)#no zero

t = timeit.Timer("with_lambda(a)","from __main__ import with_lambda,a")

print("with lambda:", t.timeit())

t = timeit.Timer("without_lambda(a)","from __main__ import without_lambda,a")

print("without lambda:", t.timeit())

t = timeit.Timer("forloop(a)","from __main__ import forloop,a")

print("for loop:", t.timeit())

t = timeit.Timer("with_lambda(b)","from __main__ import with_lambda,b")

print("with lambda (no 0):", t.timeit())

t = timeit.Timer("without_lambda(b)","from __main__ import without_lambda,b")

print("without lambda (no 0):", t.timeit())

t = timeit.Timer("forloop(b)","from __main__ import forloop,b")

print("for loop (no 0):", t.timeit())

给我

1

2

3

4

5

6('with lambda:', 17.755449056625366)

('without lambda:', 8.2084708213806152)

('for loop:', 7.4836349487304688)

('with lambda (no 0):', 22.570688009262085)

('without lambda (no 0):', 12.472226858139038)

('for loop (no 0):', 11.04065990447998)

零结果不是很有趣。有趣的是,您在什么平台上使用哪个版本的Python。

否-我刚刚添加了不带零的数字,因为我意识到wisos的答案包括零,我想知道它产生了多少不同。我在ubuntu 9.10上使用python 2.6.4。

此处给出的选项之间存在功能差异,对于空白列表,reduce答案引发TypeError,而for循环答案返回1。这是for循环答案中的错误(乘积空列表中的数字最多不超过1,即17或犰狳)。

请尝试避免将内置名称(例如list)用作变量名称。

旧答案,但是我很想编辑,因此它不使用list作为变量名...

空列表的乘积为1。en.wikipedia.org/wiki/Empty_product

@ScottGriffiths您是否还会宣称空列表的总和不为0?我强烈不同意您的主张,一个空白清单的结果为1。

@分号:取决于空列表中元素的类型(即其定义不明确)。列表[a, b, c]的总和是字符串abc,因此您可以很好地争辩说空列表的总和应为(一个空字符串)。所以TypeError似乎对我来说合适-Python语言在这里似乎与我一致...

@ScottGriffiths我应该指定我的意思是数字列表。我要说的是,空列表的总和是该类型列表的+的标识元素(对于product / *同样如此)。现在,我意识到Python是动态类型的,这会使事情变得更难,但这是在使用Haskell这样的静态类型系统的理智的语言中解决的问题。但是Python仍然只允许sum对数字进行运算,因为sum([a, b])甚至不起作用,所以我再次说0对于产品的sum和1是有意义的。

@semicolon:我正在按照OP的样式进行求和:reduce(add, [a, b, c])确实有效(您也可以尝试a + b)。内置的sum被定义为仅适用于数字,因此,如果您假设列表中没有的项目类型,则可以对空列表求和。但是您也可以使用仅适用于字符串的方法对列表求和:a + b + c可以作为.join([a, b, c])来完成,与1 + 2 + 3可以作为sum([1, 2, 3])一样,但是在这种情况下,可以对空列表求和将给出一个空字符串,而不是零。

@ScottGriffiths我确实承认,在Python中,由于缺少类型系统,事情变得更加复杂,因此我猜对空列表而言,它失败将是您因没有静态类型而付出的代价。在Haskell中,这是诸如mempty,empty之类的问题已解决的问题,并且1和0是多态的。我更争辩说,即使从实际意义上讲,像TypeError之类的东西或需要一个起始值,"数学上"一个空列表的乘积是1或"该元素的乘法身份",在Python中。

如何与Python一起将列表中的所有项目相乘?

不使用lambda:

1

2from operator import mul

reduce(mul, list, 1)

更好,更快。使用python 2.7.5

1

2

3

4

5

6

7

8

9

10from operator import mul

import numpy as np

import numexpr as ne

# from functools import reduce # python3 compatibility

a = range(1, 101)

%timeit reduce(lambda x, y: x * y, a) # (1)

%timeit reduce(mul, a) # (2)

%timeit np.prod(a) # (3)

%timeit ne.evaluate("prod(a)") # (4)

在以下配置中:

1

2

3

4a = range(1, 101) # A

a = np.array(a) # B

a = np.arange(1, 1e4, dtype=int) #C

a = np.arange(1, 1e5, dtype=float) #D

python 2.7.5的结果

1

2

3

4

5

6| 1 | 2 | 3 | 4 |

-------+-----------+-----------+-----------+-----------+

A 20.8 μs 13.3 μs 22.6 μs 39.6 μs

B 106 μs 95.3 μs 5.92 μs 26.1 μs

C 4.34 ms 3.51 ms 16.7 μs 38.9 μs

D 46.6 ms 38.5 ms 180 μs 216 μs

结果:如果将np.array用作数据结构,则np.prod是最快的(小数组为18x,大数组为250x)

使用python 3.3.2:

1

2

3

4

5

6| 1 | 2 | 3 | 4 |

-------+-----------+-----------+-----------+-----------+

A 23.6 μs 12.3 μs 68.6 μs 84.9 μs

B 133 μs 107 μs 7.42 μs 27.5 μs

C 4.79 ms 3.74 ms 18.6 μs 40.9 μs

D 48.4 ms 36.8 ms 187 μs 214 μs

python 3更慢吗?

非常有趣,谢谢。知道为什么python 3可能会更慢吗?

可能的原因:(1)Python 3 int是Python 2 long。 Python 2将使用" int"直到溢出32位; Python 3从一开始就将使用" long"。 (2)Python 3.0是"概念证明"。尽快升级到3.1!

Ive在另一台机器上重做了相同的测试:python 2.6(带有lambda :, 21.843887090682983)(没有lambda :, 9.7096879482269287)python 3.1:有lambda:24.7712180614没有lambda:10.7758350372

都失败,并带有空列表。

@bug:谢谢,固定

@RuggeroTurra理论:随着时间的流逝,Python 3.x的速度将会提高。证明:您的个人资料时间。

如果列表为空,则reduce(mul, list)失败...相反,return reduce(mul, list) if list else 1呢?

我尝试使用numpy prod(list(range(5,101)))并产生0,为什么呢?任何人都可以重现此结果吗?这是使用Anaconda安装的Python 3。

由于溢出,请尝试np.prod(range(1, 66)) * 66。要解决它,请使用np.prod(np.arange(1L, 101L, dtype=np.object))

请注意,您必须从Python 3中的functools模块导入reduce运算符。 from functools import reduce。

1reduce(lambda x, y: x * y, list, 1)

+1,但请参见@wisos关于operator.mul的答案,以获得更好的方法。

为什么operator.mul比x * y更可取?

operator.mul是一个函数,因此不仅可以替换x * y,而且可以替换整个lambda表达式(即reduce的第一个参数)

您必须执行导入from functools import reduce使其在Python 3中工作。

如果您的清单中只有数字:

1

2from numpy import prod

prod(list)

编辑:@ off99555指出,这不适用于大整数结果,在这种情况下,它返回类型为numpy.int64的结果,而基于operator.mul和reduce的伊恩·克莱尔兰德(Ian Clelland)的解决方案适用于大整数结果,因为它返回< x12>。

如果列表很短,这会比较慢

我尝试评估from numpy import prod; prod(list(range(5,101)))并输出了0,可以在Python 3上重现此结果吗?

因为在这种情况下prod返回类型为numpy.int64的结果,并且您已经为range(5,23)获得了溢出(实际上是负值)。对大整数使用基于operator.mul和reduce的@Ian Clellands解决方案(在这种情况下,它返回long似乎具有任意精度)。

@ off99555两种解决方案:或者通过执行np.prod(np.arange(5.0,101.0))从浮点类型列表开始,或者通过执行np.prod(np.array(range(5,101)).astype(np.float64))将其转换为浮点类型。请注意,NumPy使用np.float64而不是float。我不知道区别。

1

2import operator

reduce(operator.mul, list, 1)

最后一个论点(1)真的必要吗?

如果列表可能为空,则最后一个参数是必需的,否则它将引发TypeError异常。当然,有时候您会想要一个例外。

对我来说,它不带该参数就返回0,因此您也可以考虑强制执行空产品约定。

或python3中的functools.reduce(..)

好吧,如果您真的想使其成为一行而不导入任何内容,则可以执行以下操作:

1eval('*'.join(str(item) for item in list))

但是不要。

现在,让我们传递一个有趣的字符串列表(以强调"不"的原因)...

我记得在comp.lang.python上进行了很长时间的讨论(很抱歉,现在太懒了以至于无法生成指针),这些结论得出结论,您最初的product()定义是最Python的。

请注意,建议不是要每次都编写for循环,而是编写一次函数(每种归约类型)并根据需要调用它!调用归约函数非常具有Python风格-可以很好地与生成器表达式配合使用,并且由于sum()的成功引入,Python越来越多地内置了归约函数-any()和all()是最新添加的...

这个结论有点正式-从Python 3.0的内置函数中删除了reduce(),说:

"Use functools.reduce() if you really need it; however, 99 percent of the time an explicit for loop is more readable."

另请参见Python 3000中reduce()的命运,以获取来自Guido的支持引文(以及阅读该博客的Lispers的一些不那么支持的评论)。

附言如果偶然需要组合product(),请参见math.factorial()(新2.6)。

+1(就我所知)准确地解释了Python社区中的普遍情绪-虽然我绝对喜欢在这种情况下与所说的普遍情绪背道而驰,但无论如何,最好了解它们。另外,我喜欢LtU不支持Lispers的功能(我想,Id就是其中之一)。 :-)

从Python 3.8开始,prod函数已包含在标准库的math模块中:

math.prod(iterable, *, start=1)

它返回start值(默认值:1)乘以可迭代数字的乘积:

1

2

3import math

math.prod([2, 3, 4]) # 24

请注意,如果iterable为空,则将生成1(或start值(如果提供))。

这个答案的目的是提供一种在某些情况下有用的计算方法,即当a)大量数值相乘而最终产品可能非常大或非常小,并且b)您不这样做时真正关心的是确切的答案,但是有许多序列,并且希望能够根据每个人的产品订购它们。

如果要乘以列表的元素(其中l是列表),则可以执行以下操作:

1

2import math

math.exp(sum(map(math.log, l)))

现在,这种方法不像

1

2from operator import mul

reduce(mul, list)

如果您是不熟悉reduce()的数学家,则情况可能恰恰相反,但我不建议在正常情况下使用它。它也比问题中提到的product()函数可读性差(至少对非数学家而言)。

但是,如果您曾经面临下溢或上溢的风险,例如

1

2>>> reduce(mul, [10.]*309)

inf

您的目的是比较不同序列的产品,而不是了解产品是什么,然后

1

2>>> sum(map(math.log, [10.]*309))

711.49879373515785

之所以走这条路,是因为在现实世界中,用这种方法可能会出现上溢或下溢的问题几乎是不可能的。 (该计算的结果越大,如果可以计算,则乘积将越大。)

它很聪明,但是如果您有任何负值或零值,它都会失败。 :/

我用perfplot(我的一个小项目)测试了各种解决方案,发现

1numpy.prod(lst)

是迄今为止最快的解决方案(如果列表不是很短)。

vbQlU.png

复制剧情的代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48import perfplot

import numpy

from operator import mul

from functools import reduce

from itertools import accumulate

def reduce_lambda(lst):

return reduce(lambda x, y: x * y, lst)

def reduce_mul(lst):

return reduce(mul, lst)

def forloop(lst):

r = 1

for x in lst:

r *= x

return r

def numpy_prod(lst):

return numpy.prod(lst)

def itertools_accumulate(lst):

for value in accumulate(lst, mul):

pass

return value

perfplot.show(

setup=numpy.random.rand,

kernels=[

reduce_lambda,

reduce_mul,

forloop,

numpy_prod,

itertools_accumulate,

],

n_range=[2**k for k in range(15)],

xlabel='len(a)',

logx=True,

logy=True,

)

我很惊讶,没有人建议将itertools.accumulate与operator.mul一起使用。这样可以避免使用reduce,这与Python 2和3有所不同(由于Python 3需要导入functools),此外,Guido van Rossum本人也认为它是非Python语言的:

1

2

3

4

5

6

7from itertools import accumulate

from operator import mul

def prod(lst):

for value in accumulate(lst, mul):

pass

return value

例:

1

2prod([1,5,4,3,5,6])

# 1800

一种选择是使用numba和@jit或@njit装饰器。我还对您的代码进行了一两个小调整(至少在Python 3中,"列表"是一个不应用于变量名的关键字):

1

2

3

4

5

6@njit

def njit_product(lst):

p = lst[0] # first element

for i in lst[1:]: # loop over remaining elements

p *= i

return p

出于计时目的,您需要先运行一次以使用numba编译函数。通常,该函数将在首次调用时进行编译,然后在内存中调用(更快)。

1njit_product([1, 2]) # execute once to compile

现在,当您执行代码时,它将与函数的编译版本一起运行。我使用Jupyter笔记本和%timeit魔术功能为它们计时:

1

2

3

4

5product(b) # yours

# 32.7 μs ± 510 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

njit_product(b)

# 92.9 μs ± 392 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

请注意,在运行Python 3.5的计算机上,原生Python for循环实际上是最快的。在使用Jupyter笔记本电脑和%timeit魔术功能测量数字装饰性能时,这里可能会有一个技巧。我不确定上述时间是否正确,因此建议您在系统上进行尝试,看看numba是否可以提高性能。

我发现最快的方法是使用while:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34mysetup = '''

import numpy as np

from find_intervals import return_intersections

'''

# code snippet whose execution time is to be measured

mycode = '''

x = [4,5,6,7,8,9,10]

prod = 1

i = 0

while True:

prod = prod * x[i]

i = i + 1

if i == len(x):

break

'''

# timeit statement for while:

print("using while :",

timeit.timeit(setup=mysetup,

stmt=mycode))

# timeit statement for mul:

print("using mul :",

timeit.timeit('from functools import reduce;

from operator import mul;

c = reduce(mul, [4,5,6,7,8,9,10])'))

# timeit statement for mul:

print("using lambda :",

timeit.timeit('from functools import reduce;

from operator import mul;

c = reduce(lambda x, y: x * y, [4,5,6,7,8,9,10])'))

时间是:

1

2

3

4

5>>> using while : 0.8887967770060641

>>> using mul : 2.0838719510065857

>>> using lambda : 2.4227715369997895

这可能是由于列表的长度较短,可能需要更多的实验

这也可以通过欺骗来实现

1

2

3

4

5

6

7

8

9def factorial(n):

x=[]

if n <= 1:

return 1

else:

for i in range(1,n+1):

p*=i

x.append(p)

print x[n-1]

我已经解决了缩进问题,但是我认为您应该将最后一个print替换为返回值。另外,不需要将中间值存储在列表中,只需要在两次迭代之间存储p。

阶乘?列表的乘积不是阶乘。...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值