통계: Python의 조합
(nCr, 를 위한 함수를 찾을 수 없습니다.math
,numpy
또는stat
유형의 함수와 유사한 것:
comb = calculate_combinations(n, r)
실제 의 수가 에, 실조합아가조수필요서해가합제,itertools.combinations
관심 없어요.
마지막으로, 저는 요인 사용을 피하고 싶습니다. 제가 계산할 조합의 숫자가 너무 커질 수 있고 요인이 엄청날 수 있기 때문입니다.
이것은 정말 대답하기 쉬운 질문처럼 보이지만, 저는 모든 실제 조합을 생성하는 것에 대한 질문에 빠져들고 있습니다. 이것은 제가 원하는 것이 아닙니다.
2023년에 업데이트된 답변:Python 3.8 이후에 존재하고 3.11에서 훨씬 빨라진 math.comb 함수를 사용합니다.
이전 답변:scipy.special.comb(이전 버전의 scipy에서는 scipy.misc.comb)를 참조하십시오.언제exact
가말른 함수를 사용하여 많은 시간을 들이지 않고 좋은 정밀도를 얻을 수 있습니다.정확한 경우 임의 정밀 정수를 반환하므로 계산하는 데 시간이 오래 걸릴 수 있습니다.
직접 쓰는 게 어때요?한 줄 또는 그와 같은 것입니다.
from operator import mul # or mul=lambda x,y:x*y
from fractions import Fraction
def nCk(n,k):
return int( reduce(mul, (Fraction(n-i, i+1) for i in range(k)), 1) )
테스트 - Pascal의 삼각형 인쇄:
>>> for n in range(17):
... print ' '.join('%5d'%nCk(n,k) for k in range(n+1)).center(100)
...
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
1 10 45 120 210 252 210 120 45 10 1
1 11 55 165 330 462 462 330 165 55 11 1
1 12 66 220 495 792 924 792 495 220 66 12 1
1 13 78 286 715 1287 1716 1716 1287 715 286 78 13 1
1 14 91 364 1001 2002 3003 3432 3003 2002 1001 364 91 14 1
1 15 105 455 1365 3003 5005 6435 6435 5005 3003 1365 455 105 15 1
1 16 120 560 1820 4368 8008 11440 12870 11440 8008 4368 1820 560 120 16 1
>>>
되었습니다. 대체하도록 편집됨int(round(reduce(mul, (float(n-i)/(i+1) for i in range(k)), 1)))
와 함께int(reduce(mul, (Fraction(n-i, i+1) for i in range(k)), 1))
N/K N/K ▁for▁n▁▁it▁won'▁sot▁err않을/▁n지수하/실.
구글 코드에 대한 빠른 검색은 다음을 제공합니다(@Mark Byers의 답변 공식을 사용합니다).
def choose(n, k):
"""
A fast way to calculate binomial coefficients by Andrew Dalke (contrib).
"""
if 0 <= k <= n:
ntok = 1
ktok = 1
for t in xrange(1, min(k, n - k) + 1):
ntok *= n
ktok *= t
n -= 1
return ntok // ktok
else:
return 0
choose()
(0 < = ( < 쌍 ) 보다 빠릅니다(0 < = (n,k) < 1e3 쌍).scipy.misc.comb()
정확한 답변이 필요하시면.
def comb(N,k): # from scipy.comb(), but MODIFIED!
if (k > N) or (N < 0) or (k < 0):
return 0L
N,k = map(long,(N,k))
top = N
val = 1L
while (top > (N-k)):
val *= top
top -= 1
n = 1L
while (n < k+1L):
val /= n
n += 1
return val
정확한 결과와 속도를 원한다면 gmpy를 사용해보세요.gmpy.comb
당신이 요구하는 것을 정확히 해야 하고, 그것은 꽤 빠릅니다(물론, 그것은.gmpy
의 원작자, 저는 편향되어 있습니다;-).
정확한 결과를 원하는 경우 를 사용합니다.그것이 가장 빠른 방법인 것 같습니다, 솔직히.
x = 1000000
y = 234050
%timeit scipy.misc.comb(x, y, exact=True)
1 loops, best of 3: 1min 27s per loop
%timeit gmpy.comb(x, y)
1 loops, best of 3: 1.97 s per loop
%timeit int(sympy.binomial(x, y))
100000 loops, best of 3: 5.06 µs per loop
수학적 정의를 문자 그대로 번역하는 것은 많은 경우에 상당히 적절합니다(파이썬이 자동으로 큰 숫자의 산술을 사용한다는 것을 기억하십시오).
from math import factorial
def calculate_combinations(n, r):
return factorial(n) // factorial(r) // factorial(n-r)
: r=500)의의 라이너보다 .reduce
다른 답변(현재 가장 높은 투표율)에서 제안되었습니다.반면, @J.F.가 제공한 스니펫보다 성능이 뛰어납니다.세바스찬.
하는 중Python 3.8
이제 표준 라이브러리에는 이항 계수를 계산하는 함수가 포함됩니다.
수학.comb(n, k)
의 항목에서 의 항목을 할 수 의 수 반 복 개 개 없 서 에 수 하 는 택 선 의 법 방 을 n! / (k! (n - k)!)
:
import math
math.comb(10, 5) # 252
여기 다른 대안이 있습니다.이것은 원래 C++로 작성되었기 때문에 유한 정밀 정수(예: __int64)에 대해 C++로 백포트할 수 있습니다.장점은 (1) 정수 연산만 포함하고 (2) 곱셈과 나눗셈의 연속적인 쌍을 수행하여 정수 값의 부풀림을 방지한다는 것입니다.Nas Banov의 Pascal 삼각형으로 결과를 테스트해 본 결과 정답은 다음과 같습니다.
def choose(n,r):
"""Computes n! / (r! (n-r)!) exactly. Returns a python long int."""
assert n >= 0
assert 0 <= r <= n
c = 1L
denom = 1
for (num,denom) in zip(xrange(n,n-r,-1), xrange(1,r+1,1)):
c = (c * num) // denom
return c
근거:곱셈과 나눗셈의 수를 최소화하기 위해 식을 다음과 같이 다시 씁니다.
n! n(n-1)...(n-r+1)
--------- = ----------------
r!(n-r)! r!
곱셈 오버플로를 최대한 방지하기 위해 왼쪽에서 오른쪽으로 다음과 같은 엄격한 순서로 평가합니다.
n / 1 * (n-1) / 2 * (n-2) / 3 * ... * (n-r+1) / r
우리는 이 순서로 작동하는 정수 산술이 정확하다는 것을 보여줄 수 있습니다(즉, 반올림 오차 없음).
scipy.special.comb를 사용하는 것보다 실제로 약 5-8배 더 빠른 것으로 판명된 2개의 간단한 함수를 작성할 수 있습니다.실제로 추가 패키지를 가져올 필요가 없으며 기능을 쉽게 읽을 수 있습니다.비결은 메모화를 사용하여 이전에 계산된 값을 저장하고 nCr의 정의를 사용하는 것입니다.
# create a memoization dictionary
memo = {}
def factorial(n):
"""
Calculate the factorial of an input using memoization
:param n: int
:rtype value: int
"""
if n in [1,0]:
return 1
if n in memo:
return memo[n]
value = n*factorial(n-1)
memo[n] = value
return value
def ncr(n, k):
"""
Choose k elements from a set of n elements - n must be larger than or equal to k
:param n: int
:param k: int
:rtype: int
"""
return factorial(n)/(factorial(k)*factorial(n-k))
우리가 시간을 비교한다면,
from scipy.special import comb
%timeit comb(100,48)
>>> 100000 loops, best of 3: 6.78 µs per loop
%timeit ncr(100,48)
>>> 1000000 loops, best of 3: 1.39 µs per loop
동적 프로그래밍을 사용하면 시간 복잡도는 Δ(n*m)이고 공간 복잡도는 Δ(m)입니다.
def binomial(n, k):
""" (int, int) -> int
| c(n-1, k-1) + c(n-1, k), if 0 < k < n
c(n,k) = | 1 , if n = k
| 1 , if k = 0
Precondition: n > k
>>> binomial(9, 2)
36
"""
c = [0] * (n + 1)
c[0] = 1
for i in range(1, n + 1):
c[i] = 1
j = i - 1
while j > 0:
c[j] += c[j - 1]
j -= 1
return c[k]
이 프그램상있경우는이한대한으로 설정되어 있는 n
(말씀하세요.n <= N
및으로 cCr을 계산해야 에 대하여).N
시간), lru_cache를 사용하면 성능이 크게 향상됩니다.
from functools import lru_cache
@lru_cache(maxsize=None)
def nCr(n, r):
return 1 if r == 0 or r == n else nCr(n - 1, r - 1) + nCr(n - 1, r)
하는 것(으로 수행은 최대 캐구암성에는다필작다같요니합수시업까지 소요됩니다.O(N^2)
이후에 간시로 전화가 . 다음에 오는 모든 전화는nCr
돌아올 것입니다.O(1)
.
심피를 사용하면 꽤 쉽습니다.
import sympy
comb = sympy.binomial(n, r)
Python과 함께 배포된 표준 라이브러리만 사용:
import itertools
def nCk(n, k):
return len(list(itertools.combinations(range(n), k)))
직접 공식은 n이 20보다 클 때 큰 정수를 생성합니다.
그래서 또 다른 반응은:
from math import factorial
reduce(long.__mul__, range(n-r+1, n+1), 1L) // factorial(r)
길이가 긴 것을 고수함으로써 파이썬 큰 정수를 피하기 때문에 짧고 정확하며 효율적입니다.
scipy.special.comb와 비교하면 더 정확하고 빠릅니다.
>>> from scipy.special import comb
>>> nCr = lambda n,r: reduce(long.__mul__, range(n-r+1, n+1), 1L) // factorial(r)
>>> comb(128,20)
1.1965669823265365e+23
>>> nCr(128,20)
119656698232656998274400L # accurate, no loss
>>> from timeit import timeit
>>> timeit(lambda: comb(n,r))
8.231969118118286
>>> timeit(lambda: nCr(128, 20))
3.885951042175293
이 기능은 매우 최적화되어 있습니다.
def nCk(n,k):
m=0
if k==0:
m=1
if k==1:
m=n
if k>=2:
num,dem,op1,op2=1,1,k,n
while(op1>=1):
num*=op2
dem*=op1
op1-=1
op2-=1
m=num//dem
return m
그것은 상당히 큰 입력을 위해 순수 파이썬에서 할 수 있는 것만큼 빠를 것입니다.
def choose(n, k):
if k == n: return 1
if k > n: return 0
d, q = max(k, n-k), min(k, n-k)
num = 1
for n in xrange(d+1, n+1): num *= n
denom = 1
for d in xrange(1, q+1): denom *= d
return num / denom
여기 당신을 위한 효율적인 알고리즘이 있습니다.
for i = 1.....r
p = p * ( n - i ) / i
print(p)
예를 들어 nCr(30,7) = 팩트(30) / (팩트(7) * 팩트(23) = (30 * 29 * 28 * 27 * 26 * 25 * 25 * 24 ) / (1 * 2 * 3 * 4 * 5 * 6 * 7 )
1번부터 1번까지 루프를 실행하면 결과를 얻을 수 있습니다.
파이썬에서:
n,r=5,2
p=n
for i in range(1,r):
p = p*(n - i)/i
else:
p = p/(i+1)
print(p)
저는 이 스레드와 여기에 연결된 라이브러리에서 17개의 다른 기능을 타이밍했습니다.
여기에 버리기가 좀 부담스러워서, 저는 기능들의 코드를 여기 붙여넣었습니다.
제가 한 첫 번째 테스트는 파스칼의 삼각형을 100번째 줄로 만드는 것이었습니다.저는 이걸 하기 위해 시간을 100번 사용했습니다.아래 숫자는 삼각형을 한 번 만드는 데 걸린 평균 시간(초)입니다.
gmpy2.gmpy2.comb 0.0012259269999998423
math.comb 0.007063110999999935
__main__.stdfactorial2 0.011469491
__main__.scipybinom 0.0120114319999999
__main__.stdfactorial 0.012105122
__main__.scipycombexact 0.012569045999999844
__main__.andrewdalke 0.01825201100000015
__main__.rabih 0.018472497000000202
__main__.kta 0.019374668000000383
__main__.wirawan 0.029312811000000067
scipy.special._basic.comb 0.03221609299999954
__main__.jfsmodifiedscipy 0.04332894699999997
__main__.rojas 0.04395155400000021
sympy.functions.combinatorial.factorials.binomial 0.3233529779999998
__main__.nasbanov 0.593365528
__main__.pantelis300 1.7780402499999999
여기에는 16개의 기능만 있는 것을 알 수 있습니다.그 이유는recursive()
함수는 적절한 시간 내에 한 번도 이 작업을 완료할 수 없었기 때문에 테스트할 때 제외해야 했습니다.정말로, 몇 시간 동안 계속되고 있습니다.
또한 위의 모든 기능이 지원하지 않는 다양한 유형의 입력에 대한 시간을 측정했습니다.nCr은 계산 비용이 많이 들고 성격이 급하기 때문에 테스트를 10번씩만 실행했다는 것을 기억하세요.
n에 대한 부분 값
__main__.scipybinom 0.011481370000000001
__main__.kta 0.01869513999999999
sympy.functions.combinatorial.factorials.binomial 6.33897291
r에 대한 부분 값
__main__.scipybinom 0.010960040000000504
scipy.special._basic.comb 0.03681254999999908
sympy.functions.combinatorial.factorials.binomial 3.2962564499999987
n 및 r에 대한 부분 값
__main__.scipybinom 0.008623409999998444
sympy.functions.combinatorial.factorials.binomial 3.690936439999999
n에 대한 음수 값
gmpy2.gmpy2.comb 0.010770989999997482
__main__.kta 0.02187850000000253
__main__.rojas 0.05104292999999984
__main__.nasbanov 0.6153183200000001
sympy.functions.combinatorial.factorials.binomial 3.0460310799999943
n에 대한 음수 부분 값, r에 대한 소수 부분 값
sympy.functions.combinatorial.factorials.binomial 3.7689941699999965
최대 속도와 다용성을 위한 현재 최고의 솔루션은 입력에 따라 서로 다른 알고리즘 중에서 선택할 수 있는 하이브리드 기능입니다.
def hybrid(n: typing.Union[int, float], k: typing.Union[int, float]) -> typing.Union[int, float]:
# my own custom hybrid solution
def is_integer(n):
return isinstance(n, int) or n.is_integer()
if k < 0:
raise ValueError("k cannot be negative.")
elif n == 0:
return 0
elif k == 0 or k == n:
return 1
elif is_integer(n) and is_integer(k):
return int(gmpy2.comb(int(n), int(k)))
elif n > 0:
return scipy.special.binom(n, k)
else:
return float(sympy.binomial(n, k))
부터sympy.binomial()
너무 느리고, 진정한 이상적인 해결책은 코드를 결합하는 것입니다.scipy.special.binom()
분수에 대해 잘 수행되는 것과gmpy2.comb()
int. scipy의 func와 gimpy2의 func 모두 제가 잘 모르는 C로 작성되었습니다.
내장된 메모 장식기를 사용한 @killer T2333 코드입니다.
from functools import lru_cache
@lru_cache()
def factorial(n):
"""
Calculate the factorial of an input using memoization
:param n: int
:rtype value: int
"""
return 1 if n in (1, 0) else n * factorial(n-1)
@lru_cache()
def ncr(n, k):
"""
Choose k elements from a set of n elements,
n must be greater than or equal to k.
:param n: int
:param k: int
:rtype: int
"""
return factorial(n) // (factorial(k) * factorial(n - k))
print(ncr(6, 3))
언급URL : https://stackoverflow.com/questions/3025162/statistics-combinations-in-python
'programing' 카테고리의 다른 글
오라클에 따옴표를 포함하는 문자열 삽입 (0) | 2023.07.21 |
---|---|
스프링 저장 프로시저 - 프로시저에서 반환되는 결과는 항상 비어 있습니다. (0) | 2023.07.21 |
ExecutorService 'applicationTaskExecuter' 종료 중 (0) | 2023.07.21 |
봄 부팅 웹 소켓에서 특정 사용자에게 알림 보내기 (0) | 2023.07.21 |
이미 생성된 가상 환경에서 파이썬 경로를 어떻게 설정합니까? (0) | 2023.07.21 |