IT박스

목록의 제품 반품

itboxs 2020. 6. 15. 21:59
반응형

목록의 제품 반품


다음을 수행하는 더 간결하고 효율적이거나 단순히 파이썬적인 방법이 있습니까?

def product(list):
    p = 1
    for i in list:
        p *= i
    return p

편집하다:

실제로 이것은 operator.mul을 사용하는 것보다 약간 빠릅니다.

from 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())

나에게 준다

('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)

람다를 사용하지 않고 :

from operator import mul
reduce(mul, list, 1)

더 좋고 빠릅니다. 파이썬 2.7.5로

from 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)

다음 구성에서 :

a = 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 |
------- + ----------- + ----------- ++ ---------- + ------ ----- +
 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.34ms 3.51ms 16.7µs 38.9µs
 D 46.6ms 38.5ms 180µs 216µs

결과 : 데이터 구조로 np.prod사용 np.array하는 경우 가장 빠릅니다 (작은 배열의 경우 18x, 큰 배열의 경우 250x).

파이썬 3.3.2로 :


       | 1 | 2 | 3 | 4 |
------- + ----------- + ----------- ++ ---------- + ------ ----- +
 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.79ms 3.74ms 18.6µs 40.9µs
 D 48.4ms 36.8ms 187µs 214µs

파이썬 3이 느려 집니까?


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

목록에 숫자 만있는 경우 :

from numpy import prod
prod(list)

편집 : @ off99555에 의해 지적 된대로 이것은 큰 정수 결과에 대해 작동하지 않습니다.이 경우 numpy.int64Ian Clelland의 솔루션을 기반으로 operator.mul하고 reduce큰 정수 결과에 대해 작동 하는 동안 형식 결과를 반환합니다 long.


import operator
reduce(operator.mul, list, 1)

실제로 가져 오기없이 한 줄로 만들고 싶다면 다음을 수행하십시오.

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

그러나하지 마십시오.


comp.lang.python (죄송하지만 포인터를 생성하기에는 너무 게으르다)에 대한 오랜 토론을 기억 하며 원래 product()정의가 가장 Pythonic 이라고 결론 내 렸습니다 .

제안은 당신이 그것을 원할 때마다 for 루프를 작성하는 것이 아니라 함수를 한 번 (축소 유형별로) 작성하고 필요에 따라 호출하는 것입니다! 축소 함수를 호출하는 것은 매우 Pythonic입니다. 생성기 표현식과 잘 작동하며,을 성공적으로 도입 한 sum()이후 Python은 점점 더 기본 제공되는 축소 함수를 계속 증가 any()시키고 all()있습니다.

이 결론은 좀 공식입니다 - reduce()제거 말, 파이썬 3.0에 내장 명령에서 :

" functools.reduce()실제로 필요한 경우 사용하십시오 . 그러나 명시 적 for 루프가 더 읽기 쉬운 시간의 99 %입니다."

Guido의 지원 인용문 및 해당 블로그를 읽는 Lispers의 의견이 적지 않은 내용은 Python 3000의 reduce () 운명을 참조하십시오 .

우연히 product()조합론 이 필요한 경우 PS math.factorial()(신규 2.6)를 참조하십시오 .


시작 Python 3.8하여 prod함수가 math표준 라이브러리 모듈에 포함되었습니다 .

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

startiterable 숫자 값 (기본값 : 1) 곱의 곱을 반환합니다 .

import math

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

iterable이 비어 있으면 1(또는 start제공된 경우 값) 생성됩니다.


이 답변의 목적은 특정 상황에서 유용한 계산을 제공하는 것입니다. 즉, a) 최종 제품이 매우 크거나 작을 수 있도록 많은 수의 값을 곱한 경우 b) 실제로 정확한 답에 신경 쓰지 말고 대신 많은 수의 시퀀스를 가지며 각 제품에 따라 주문할 수 있기를 원합니다.

리스트의 요소를 곱하려면 (여기서 l은리스트 임) 다음을 수행 할 수 있습니다.

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

이제 그 접근 방식은 읽을 수 없습니다

from operator import mul
reduce(mul, list)

reduce ()에 익숙하지 않은 수학자라면 그 반대 일 수도 있지만 정상적인 상황에서는 사용하지 않는 것이 좋습니다. 또한 질문에 언급 된 product () 함수보다 읽기 쉽지 않습니다 (적어도 비 수학자에게는).

그러나 다음과 같이 언더 플로 또는 오버플로 위험이있는 상황에 처한 경우

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

당신의 목적은 제품이 무엇인지 아는 것이 아니라 다른 시퀀스의 제품을 비교하는 것입니다.

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

이 접근 방식으로 오버플로 또는 언더 플로되는 실제 문제를 일으키는 것은 사실상 불가능하기 때문에 갈 길입니다. (계산 결과가 클수록 계산할 있는 경우 제품이 커집니다 .)


perfplot (작은 프로젝트)으로 다양한 솔루션을 테스트 한 결과

numpy.prod(lst)

입니다 지금까지 가장 빠른 솔루션 (목록은 매우 짧은 아닌 경우).

여기에 이미지 설명을 입력하십시오


줄거리를 재현하는 코드 :

import 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,
    )

I am surprised no-one has suggested using itertools.accumulate with operator.mul. This avoids using reduce, which is different for Python 2 and 3 (due to the functools import required for Python 3), and moreover is considered un-pythonic by Guido van Rossum himself:

from itertools import accumulate
from operator import mul

def prod(lst):
    for value in accumulate(lst, mul):
        pass
    return value

Example:

prod([1,5,4,3,5,6])
# 1800

One option is to use numba and the @jit or @njit decorator. I also made one or two little tweaks to your code (at least in Python 3, "list" is a keyword that shouldn't be used for a variable name):

@njit
def njit_product(lst):
    p = lst[0]  # first element
    for i in lst[1:]:  # loop over remaining elements
        p *= i
    return p

For timing purposes, you need to run once to compile the function first using numba. In general, the function will be compiled the first time it is called, and then called from memory after that (faster).

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

이제 코드를 실행하면 컴파일 된 버전의 함수로 코드가 실행됩니다. Jupyter 노트북과 %timeit마법 기능을 사용하여 시간을 정했습니다 .

product(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로 꾸며진 성능을 측정 할 때 트릭이있을 수 있습니다 . 위의 타이밍이 올바른지 확실하지 않으므로 시스템에서 시도해보고 numba가 성능을 향상시키는 지 확인하는 것이 좋습니다.


내가 찾은 가장 빠른 방법은 다음을 사용하는 것입니다.

mysetup = '''
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])'))

타이밍은 다음과 같습니다.

>>> using while : 0.8887967770060641

>>> using mul : 2.0838719510065857

>>> using lambda : 2.4227715369997895

이것은 또한 부정 행위를 통해 작동

def 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]    

참고 URL : https://stackoverflow.com/questions/2104782/returning-the-product-of-a-list

반응형