পরিসংখ্যান: পাইথনে সংমিশ্রণগুলি


122

আমি পাইথন মধ্যে combinatorials (nCr) গনা প্রয়োজন কিন্তু কিছু করার যে ফাংশন খুঁজে পাচ্ছি না math, numpyবা stat লাইব্রেরি। ধরণের কোনও ফাংশনের মতো:

comb = calculate_combinations(n, r)

আমার সম্ভাব্য সংমিশ্রণের সংখ্যা দরকার, আসল সংমিশ্রণগুলি নয়, তাই itertools.combinationsআমার আগ্রহ নেই।

পরিশেষে, আমি ফ্যাকটোরিয়ালগুলি ব্যবহার করা এড়াতে চাই, কারণ যে সংখ্যার জন্য আমি সংমিশ্রণগুলি গণনা করব সেগুলি খুব বড় হতে পারে এবং ঘটনাক্রমে সংঘবদ্ধ হতে চলেছে।

এটিকে প্রশ্নের উত্তর দেওয়া সত্যিই সহজ বলে মনে হচ্ছে, তবে আমি সমস্ত আসল সংমিশ্রণ উত্পন্ন করার প্রশ্নে ডুবে যাচ্ছি যা আমি চাই না।

উত্তর:


121

স্কিপি.স্পেশিয়াল.কম (স্কিপি.মিসিক.কম) এর পুরানো সংস্করণগুলিতে দেখুন । কখন exactমিথ্যা হয়, এটি বেশি সময় না নিয়ে ভাল নির্ভুলতা পেতে গামাল ফাংশনটি ব্যবহার করে। সঠিক ক্ষেত্রে এটি একটি স্বেচ্ছাসেবী-নির্ভুলতা পূর্ণসংখ্যার ফেরত দেয় যা গণনা করতে অনেক সময় নিতে পারে।


5
scipy.misc.combপক্ষে অবচিত scipy.special.combযেহেতু সংস্করণ 0.10.0
দিলোয়ার

120

নিজেই লিখছেন না কেন? এটি একটি ওয়ানলাইনার বা এ জাতীয়:

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

পরীক্ষা - পাস্কেলের ত্রিভুজ মুদ্রণ:

>>> 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))তাই এটা বড় এন / ট জন্য মাত্রই ভুল করে না করবে না


26
সহজ কিছু লেখার পরামর্শ দেওয়ার জন্য, হ্রাস ব্যবহারের জন্য এবং
প্যাসেল

6
-1 কারণ এই উত্তরটি ভুল: মুদ্রণ ফ্যাক্টরিয়াল (54) / (ফ্যাক্টরিয়াল (54 - 27)) / ফ্যাকটোরিয়াল (27) == এনসিকে (54, 27) মিথ্যা দেয়।
রবার্ট রাজা

3
@ রবার্টকিং - ঠিক আছে, আপনি ক্ষুদ্র এবং প্রযুক্তিগতভাবে উভয়ই সঠিক ছিলেন। আমি যা করেছি তা বোঝানো হয়েছে কীভাবে নিজের ফাংশনটি লেখার উদাহরণ হিসাবে; আমি জানতাম যে ভাসমান পয়েন্ট যথার্থতার কারণে এটি যথেষ্ট পরিমাণে এন এবং কে এর পক্ষে সঠিক নয়। তবে আমরা এটি ঠিক করতে পারি - উপরে দেখুন, এখন এটি বড় সংখ্যার জন্য ভুল হওয়া উচিত নয়
নাস বানভ

9
এটি সম্ভবত হাস্কেলের দ্রুত হবে, তবে দুর্ভাগ্যক্রমে পাইথন নয়। অন্যান্য উত্তরগুলির তুলনায় এটি আসলে বেশ ধীর, যেমন @ অ্যালেক্স মার্তেলি, জেএফ সেবাস্তিয়ান এবং আমার নিজের।
টড ওভেন

9
পাইথন 3 এর জন্য, আমাকেও করতে হয়েছিল from functools import reduce
ভেলিজার হরিস্টভ

52

গুগল কোডে একটি দ্রুত অনুসন্ধান দেয় (এটি @ মার্ক বাইয়ার্সের উত্তর থেকে সূত্র ব্যবহার করে ):

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()scipy.misc.comb()আপনার যদি সঠিক উত্তর প্রয়োজন হয় তার চেয়ে 10 গুণ দ্রুত (সমস্ত 0 <= (n, কে) <1e3 জোড়া পরীক্ষিত) is

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

একটি দুর্দান্ত সমাধান যা কোনও পিকেজি প্রয়োজন হয় না
এডওয়ার্ড নেওয়েল

2
: অবগতির জন্য: সূত্র উল্লেখ এখানে en.wikipedia.org/wiki/...
jmiserez

এই chooseফাংশনটির আরও বেশি ভোট হওয়া উচিত! পাইথন ৩.৮-তে ম্যাথ.কম রয়েছে, তবে আমি চ্যালেঞ্জের জন্য পাইথন ৩.6 ব্যবহার করতে হয়েছিল এবং কোনও প্রয়োগই খুব বড় পূর্ণসংখ্যার সঠিক ফলাফল দেয়নি। এই এক এটি করে এবং দ্রুত করে!
11

42

আপনি সঠিক ফলাফল চান এবং গতি, চেষ্টা gmpy - gmpy.combঠিক কি করা উচিত আপনি যা জিজ্ঞেস করে, এবং এটি বেশ দ্রুত (অবশ্যই আছে, যেমন gmpy, আমি এর মূল লেখক am পক্ষপাতমূলক ;-)।


6
বস্তুত, gmpy2.comb()10 বার চেয়ে দ্রুত choose(): কোডের জন্য আমার উত্তর থেকে for k, n in itertools.combinations(range(1000), 2): f(n,k)যেখানে f()পারেন হয় gmpy2.comb()বা choose()পাইথন 3. উপর
JFS

যেহেতু আপনি প্যাকেজের লেখক, আমি আপনাকে ভাঙা লিঙ্কটি ঠিক করতে দেব যাতে এটি সঠিক জায়গায়
দেখায়

@ সেলডমনিডি, কোড.google.com.com এর লিঙ্কটি একটি সঠিক জায়গা (যদিও সাইটটি এখন আর্কাইভ মোডে রয়েছে)। অবশ্যই সেখান থেকে গিথুব অবস্থান, github.com/aleaxit/gmpy , এবং পাইপিআই এক, পাইপি.পিথন.আর.পিপি / গম্পি 2 , এটি উভয়ের সাথে সংযুক্ত হিসাবে খুঁজে পাওয়া সহজ! -)
অ্যালেক্স মার্টেলি

@ অ্যালেক্সমার্টেলি এই বিভ্রান্তির জন্য দুঃখিত। জাভাস্ক্রিপ্ট (নির্বাচিতভাবে) অক্ষম করা থাকলে পৃষ্ঠা 404 প্রদর্শন করে। আমার ধারণা, এটি এত সহজেই সংরক্ষণাগারযুক্ত গুগল কোড প্রকল্পের উত্সগুলি অন্তর্ভুক্ত করা থেকে দুর্বৃত্তদের এআইএসকে নিরুৎসাহিত করবে?
সেলডমনিডি

28

আপনি যদি সঠিক ফলাফল চান তবে ব্যবহার করুন sympy.binomial। মনে হচ্ছে এটি দ্রুততম পদ্ধতি, হাত নীচে।

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

22

গাণিতিক সংজ্ঞা একটি আক্ষরিক অনুবাদ অনেক ক্ষেত্রে যথেষ্ট পর্যাপ্ত (মনে আছে যে পাইথন স্বয়ংক্রিয়ভাবে বড় সংখ্যার গাণিতিক ব্যবহার করবে):

from math import factorial

def calculate_combinations(n, r):
    return factorial(n) // factorial(r) // factorial(n-r)

কিছু পরীক্ষার জন্য আমি পরীক্ষিত (যেমন এন = 1000 আর = 500) এটি reduceঅন্য লাইনারের (বর্তমানে সর্বাধিক ভোট দেওয়া) উত্তরে প্রস্তাবিত এক লাইনারের চেয়ে 10 গুণ বেশি দ্রুত ছিল । অন্যদিকে, এটি @ জেএফ সেবাস্তিয়ান সরবরাহ করেছেন স্নিপিট দ্বারা আউট-পারফর্ম করা হয়েছে।


11

শুরু হচ্ছে Python 3.8, স্ট্যান্ডার্ড লাইব্রেরিতে এখন math.combদ্বিপদী সহগের গুণন করার জন্য ফাংশন অন্তর্ভুক্ত রয়েছে :

math.comb (এন, কে)

কোন পুনরাবৃত্তি ছাড়াই এন আইটেম থেকে কে আইটেম নির্বাচন করার উপায় সংখ্যা
n! / (k! (n - k)!):

import math
math.comb(10, 5) # 252

10

এখানে অন্য বিকল্প। এটি প্রথমত সি ++ এ লেখা হয়েছিল, সুতরাং এটি সসীম-নির্ভুলতা পূর্ণসংখ্যার (যেমন __int64) এর জন্য সি ++ এ ব্যাকপোর্ট করা যায়। সুবিধাটি হ'ল (১) এতে কেবল পূর্ণসংখ্যার ক্রিয়াকলাপ জড়িত এবং (২) এটি ক্রমাগত জোড়গুলি এবং গুণকে ভাগ করে পূর্ণসংখ্যার মান স্ফীত করা থেকে বিরত থাকে। আমি নাস বানভের পাস্কাল ত্রিভুজ দিয়ে ফলাফলটি পরীক্ষা করেছি, এটির সঠিক উত্তর পেয়েছে:

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

আমরা দেখতে পারি যে এই ক্রমটিতে পরিচালিত পূর্ণসংখ্যার গাণিতিকটি সঠিক (অর্থাত্ কোনও রাউন্ডঅফ ত্রুটি নেই)।


5

ডায়নামিক প্রোগ্রামিং ব্যবহার করে সময় জটিলতা হ'ল Θ (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]

4

যদি আপনার প্রোগ্রামটির উপরের আবদ্ধ থাকে n(বলুন n <= N) এবং বার 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)সময় লাগে takes পরবর্তী যে কোনও কলগুলি nCrফিরে আসবে O(1)


4

আপনি 2 টি সাধারণ ফাংশন লিখতে পারেন যা প্রকৃতপক্ষে স্কিপি.স্পেশাল.কম ব্যবহারের চেয়ে প্রায় 5-8 গুণ দ্রুত হতে পারে । আসলে, আপনার কোনও অতিরিক্ত প্যাকেজ আমদানি করার দরকার নেই, এবং ফাংশনটি খুব সহজেই পঠনযোগ্য। কৌতুক পূর্বে নির্ণিত মান সংরক্ষণ করতে memoization ব্যবহার করতে হয়, এবং সংজ্ঞা ব্যবহার 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

আজকাল ফান্টুলগুলিতে একটি স্মৃতিসজ্জা রয়েছে যার নাম lru_cache যা আপনার কোডটি সহজীকরণ করতে পারে?
সজারু উন্মাদ


2

পাইথনের সাথে বিতরণ করা কেবলমাত্র স্ট্যান্ডার্ড গ্রন্থাগার ব্যবহার :

import itertools

def nCk(n, k):
    return len(list(itertools.combinations(range(n), k)))

3
আমি মনে করি না এর সময়ের জটিলতা (এবং মেমরির ব্যবহার) গ্রহণযোগ্য।
xmcp

2

যখন 20 এর চেয়ে বড় হয় তখন সরাসরি সূত্রটি বড় পূর্ণসংখ্যার উত্পাদন করে।

সুতরাং, অন্য একটি প্রতিক্রিয়া:

from math import factorial

reduce(long.__mul__, range(n-r+1, n+1), 1L) // factorial(r)

সংক্ষিপ্ত, নির্ভুল এবং দক্ষ কারণ এটি দীর্ঘায়ুগুলির সাথে লেগে থাকা অজগরটি বড় পূর্ণসংখ্যার এড়ায়।

স্কিপি.স্পেশাল.কম.বিয়ের সাথে তুলনা করার সময় এটি আরও নির্ভুল এবং দ্রুত:

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

এটা ভুল! যদি n == R, ফলাফলের এই কোড আয় 0. 1. হওয়া উচিত
reyammer

আরও স্পষ্টভাবে, এটি এর range(n-r+1, n+1)পরিবর্তে হওয়া উচিত range(n-r,n+1)
রেয়ামার

1

এটি বিল্টিন মেমোয়াইজেশন ডেকরেটার ব্যবহার করে @ কিলারটি 2333 কোড।

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

1

আপনার জন্য এখানে একটি কার্যকর অ্যালগরিদম

for i = 1.....r

   p = p * ( n - i ) / i

print(p)

উদাহরণস্বরূপ এনসিআর (30,7) = সত্য (30) / (তথ্য (7) * সত্য (23)) = (30 * 29 * 28 * 27 * 26 * 25 * 24) / (1 * 2 * 3 * 4 * 5 * 6 * 7)

সুতরাং 1 থেকে r পর্যন্ত লুপটি চালান ফলাফল পেতে পারে।


0

যুক্তিযুক্ত বড় ইনপুটগুলির জন্য আপনি খাঁটি অজগরটিতে এটি যতটা দ্রুত করতে পারেন সম্ভবত:

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

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.