Биномиальный коэффициент Python
import math
x = int(input("Enter a value for x: "))
y = int(input("Enter a value for y: "))
if y == 1 or y == x:
print(1)
if y > x:
print(0)
else:
a = math.factorial(x)
b = math.factorial(y)
div = a // (b*(x-y))
print(div)
Эта программа с биномиальным коэффициентом работает, но когда я ввожу два одинаковых числа, которые, как предполагается, равны 1, или когда y больше, чем x, оно должно равняться 0
Ответы
Ответ 1
Этот вопрос старый, но, поскольку он поднимается высоко над результатами поиска, я scipy
, что у scipy
есть две функции для вычисления биномиальных коэффициентов:
-
scipy.special.binom()
-
scipy.special.comb()
import scipy.special
# the two give the same results
scipy.special.binom(10, 5)
# 252.0
scipy.special.comb(10, 5)
# 252.0
scipy.special.binom(300, 150)
# 9.375970277281882e+88
scipy.special.comb(300, 150)
# 9.375970277281882e+88
# ...but with 'exact == True'
scipy.special.comb(10, 5, exact=True)
# 252
scipy.special.comb(300, 150, exact=True)
# 393759702772827452793193754439064084879232655700081358920472352712975170021839591675861424
Обратите внимание, что scipy.special.comb(exact=True)
использует целые числа Python, и поэтому он может обрабатывать произвольно большие результаты!
По скорости три версии дают несколько разные результаты:
num = 300
%timeit [[scipy.special.binom(n, k) for k in range(n + 1)] for n in range(num)]
# 52.9 ms ± 107 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit [[scipy.special.comb(n, k) for k in range(n + 1)] for n in range(num)]
# 183 ms ± 814 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)each)
%timeit [[scipy.special.comb(n, k, exact=True) for k in range(n + 1)] for n in range(num)]
# 180 ms ± 649 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
(и для n = 300
, биномиальные коэффициенты слишком велики, чтобы быть правильно представленными с использованием чисел с float64
, как показано выше).
Ответ 2
Вот версия, которая на самом деле использует правильную формулу. :)
#! /usr/bin/env python
''' Calculate binomial coefficient xCy = x! / (y! (x-y)!)
'''
from math import factorial as fac
def binomial(x, y):
try:
binom = fac(x) // fac(y) // fac(x - y)
except ValueError:
binom = 0
return binom
#Print Pascal triangle to test binomial()
def pascal(m):
for x in range(m + 1):
print([binomial(x, y) for y in range(x + 1)])
def main():
#input = raw_input
x = int(input("Enter a value for x: "))
y = int(input("Enter a value for y: "))
print(binomial(x, y))
if __name__ == '__main__':
#pascal(8)
main()
...
Вот альтернативная версия binomial()
я написал несколько лет назад, в которой не используется math.factorial()
, которой не было в старых версиях Python. Однако он возвращает 1, если r не находится в диапазоне (0, n + 1).
def binomial(n, r):
''' Binomial coefficient, nCr, aka the "choose" function
n! / (r! * (n - r)!)
'''
p = 1
for i in range(1, min(r, n - r) + 1):
p *= n
p //= i
n -= 1
return p
Ответ 3
Итак, этот вопрос возникает первым, если вы ищете "Реализовать биномиальные коэффициенты в Python". Только этот ответ во второй части содержит эффективную реализацию, основанную на мультипликативной формуле. Эта формула выполняет минимальное количество умножений. Приведенная ниже функция не зависит от каких-либо встроенных функций или импорта:
def fcomb0(n, k):
'''
Compute the number of ways to choose $k$ elements out of a pile of $n.$
Use an iterative approach with the multiplicative formula:
$$\frac{n!}{k!(n - k)!} =
\frac{n(n - 1)\dots(n - k + 1)}{k(k-1)\dots(1)} =
\prod_{i = 1}^{k}\frac{n + 1 - i}{i}$$
Also rely on the symmetry: $C_n^k = C_n^{n - k},$ so the product can
be calculated up to $\min(k, n - k).$
:param n: the size of the pile of elements
:param k: the number of elements to take from the pile
:return: the number of ways to choose k elements out of a pile of n
'''
# When k out of sensible range, should probably throw an exception.
# For compatibility with scipy.special.{comb, binom} returns 0 instead.
if k < 0 or k > n:
return 0
if k == 0 or k == n:
return 1
total_ways = 1
for i in range(min(k, n - k)):
total_ways = total_ways * (n - i) // (i + 1)
return total_ways
Наконец, если вам нужны еще большие значения и вы не против торговать с какой-то точностью, возможно, вам подходит приближение Стирлинга.
Ответ 4
Обратите внимание, что начиная с Python 3.8
, стандартная библиотека предоставляет функцию math.comb
для вычисления биномиального коэффициента:
math.comb(n, k)
это количество способов выбрать k элементов из n элементов без повторения
n! / (k! (n - k)!)
:
import math
math.comb(10, 5) # 252
math.comb(10, 10) # 1
Ответ 5
Ваша программа продолжит выполнение второго оператора if
в случае y == x
, вызвав ZeroDivisionError
. Вы должны сделать утверждения взаимоисключающими; способ сделать это - использовать elif
( "else if" ) вместо if
:
import math
x = int(input("Enter a value for x: "))
y = int(input("Enter a value for y: "))
if y == x:
print(1)
elif y == 1: # see georg comment
print(x)
elif y > x: # will be executed only if y != 1 and y != x
print(0)
else: # will be executed only if y != 1 and y != x and x <= y
a = math.factorial(x)
b = math.factorial(y)
c = math.factorial(x-y) # that appears to be useful to get the correct result
div = a // (b * c)
print(div)
Ответ 6
Вот функция, которая рекурсивно вычисляет биномиальные коэффициенты, используя условные выражения
def binomial(n,k):
return 1 if k==0 else (0 if n==0 else binomial(n-1, k) + binomial(n-1, k-1))
Ответ 7
Для Python 3 у scipy есть функция scipy.special.comb, которая может генерировать с плавающей точкой, а также точные целочисленные результаты
import scipy.special
res = scipy.special.comb(x, y, exact=True)
Смотрите документацию для scipy.special.comb.
Для Python 2 функция находится в scipy.misc, и она работает одинаково:
import scipy.misc
res = scipy.misc.comb(x, y, exact=True)
Ответ 8
Как насчет этого?:) Он использует правильную формулу, избегает math.factorial
и принимает меньше операций умножения:
import math
import operator
product = lambda m,n: reduce(operator.mul, xrange(m, n+1), 1)
x = max(0, int(input("Enter a value for x: ")))
y = max(0, int(input("Enter a value for y: ")))
print product(y+1, x) / product(1, x-y)
Кроме того, чтобы избежать арифметики с большими целыми числами, вы можете использовать числа с плавающей запятой, конвертировать
product(a[i])/product(b[i])
до product(a[i]/b[i])
и перепишите вышеуказанную программу как:
import math
import operator
product = lambda iterable: reduce(operator.mul, iterable, 1)
x = max(0, int(input("Enter a value for x: ")))
y = max(0, int(input("Enter a value for y: ")))
print product(map(operator.truediv, xrange(y+1, x+1), xrange(1, x-y+1)))
Ответ 9
Я рекомендую использовать динамическое программирование (DP) для вычисления биномиальных коэффициентов. В отличие от прямого вычисления, он избегает умножения и деления больших чисел. В дополнение к рекурсивному решению он сохраняет ранее решенные перекрывающиеся подзадачи в таблице для быстрого поиска. В приведенном ниже коде показаны реализации восходящего (табличного) DP и нисходящего (запомненного) DP для вычисления биномиальных коэффициентов.
def binomial_coeffs1(n, k):
#top down DP
if (k == 0 or k == n):
return 1
if (memo[n][k] != -1):
return memo[n][k]
memo[n][k] = binomial_coeffs1(n-1, k-1) + binomial_coeffs1(n-1, k)
return memo[n][k]
def binomial_coeffs2(n, k):
#bottom up DP
for i in range(n+1):
for j in range(min(i,k)+1):
if (j == 0 or j == i):
memo[i][j] = 1
else:
memo[i][j] = memo[i-1][j-1] + memo[i-1][j]
#end if
#end for
#end for
return memo[n][k]
def print_array(memo):
for i in range(len(memo)):
print('\t'.join([str(x) for x in memo[i]]))
#main
n = 5
k = 2
print("top down DP")
memo = [[-1 for i in range(6)] for j in range(6)]
nCk = binomial_coeffs1(n, k)
print_array(memo)
print("C(n={}, k={}) = {}".format(n,k,nCk))
print("bottom up DP")
memo = [[-1 for i in range(6)] for j in range(6)]
nCk = binomial_coeffs2(n, k)
print_array(memo)
print("C(n={}, k={}) = {}".format(n,k,nCk))
Примечание: размер таблицы памятки для отображения установлен на небольшое значение (6), его следует увеличить, если вы вычисляете биномиальные коэффициенты для больших n и k.
Ответ 10
Самый простой способ - использовать формулу Multiplicative. Это работает для (n, n) и (n, 0), как и ожидалось.
def coefficient(n,k):
c = 1.0
for i in range(1, k+1):
c *= float((n+1-i))/float(i)
return c
Мультипликативная формула
Ответ 11
Немного сокращенный мультипликативный вариант, заданный PM 2Ring и alisianoi. Работает с Python 3 и не требует никаких пакетов.
def comb(n, k):
# Remove the next two lines is out-of-range check is not needed
if k < 0 or k > n:
return None
x = 1
for i in range(min(k, n - k)):
x = x*(n - i)//(i + 1)
return x
Или
from functools import reduce
def comb(n, k):
return (None if k < 0 or k > n else
reduce(lambda x, i: x*(n - i)//(i + 1), range(min(k, n - k)), 1))
Деление выполняется сразу после умножения, чтобы не накапливать большие числа.
Ответ 12
Было бы неплохо применить рекурсивное определение, как в ответе Вадима Смолякова, в сочетании с DP (динамическое программирование), но для последнего вы можете применить декоратор lru_cache из модуля functools:
import functools
@functools.lru_cache(maxsize = None)
def binom(n,k):
if k == 0: return 1
if n == k: return 1
return binom(n-1,k-1)+binom(n-1,k)