web-dev-qa-db-fra.com

Coefficient binomial en 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)  

Ce programme coeeficient binomial fonctionne, mais lorsque j’en saisis deux du même nombre qui est supposé égal à 1 ou lorsque y est supérieur à x, il est supposé égal à 0. le programme doit être légèrement modifié si quelqu'un peut m'aider

18
user3396351

Votre programme continuera avec la deuxième instruction if dans le cas de y == x, provoquant une ZeroDivisionError. Vous devez faire les déclarations mutuellement exclusives; Pour ce faire, utilisez Elif ("else if") au lieu de 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's 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)  
2
Tim Pietzcker

Cette question est ancienne, mais comme elle figure en bonne place dans les résultats de recherche, je soulignerai que scipy a deux fonctions pour calculer les coefficients binomiaux:

  1. scipy.special.binom()
  2. 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
    

Notez que scipy.special.comb(exact=True) utilise des entiers Python et qu'il peut donc gérer des résultats arbitrairement volumineux!

En termes de vitesse, les trois versions donnent des résultats quelque peu différents:

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)

(et pour n = 300, les coefficients binomiaux sont trop importants pour être représentés correctement avec les nombres float64, comme indiqué ci-dessus).

67
Shaun Coutts

Voici une version qui utilise réellement la formule correcte . :)

#! /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's 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 __== '__main__':
    #Pascal(8)
    main()

...

Voici une autre version de binomial() que j'ai écrite il y a plusieurs années et qui n'utilise pas math.factorial(), qui n'existait pas dans les anciennes versions de Python. Cependant, il retourne 1 si r n'est pas dans la plage (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
18
PM 2Ring

Donc, cette question revient en premier si vous recherchez "Implémentez des coefficients binomiaux en Python". Seule this answer dans sa deuxième partie contient une implémentation efficace reposant sur la formule multiplicative . Cette formule effectue le nombre minimum de multiplications. La fonction ci-dessous ne dépend d'aucun élément intégré ou importé:

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

Enfin, si vous avez besoin de valeurs encore plus grandes et que l’échange de précision ne vous dérange pas, l’approximation de Stirling est probablement la solution.

3
alisianoi

Pour Python 3, scipy a la fonction scipy.special.comb, qui peut produire des résultats en virgule flottante ainsi que des entiers exacts.

import scipy.special

res = scipy.special.comb(x, y, exact=True)

Voir la documentation de scipy.special.comb .

Pour Python 2, la fonction est située dans scipy.misc et fonctionne de la même manière:

import scipy.misc

res = scipy.misc.comb(x, y, exact=True)
2
Slava

Voici une fonction qui calcule de manière récursive les coefficients binomiaux à l'aide d'expressions conditionnelles

def binomial(n,k):
    return 1 if k==0 else (0 if n==0 else binomial(n-1, k) + binomial(n-1, k-1))
1
merrais

Qu'en est-il de celui-ci? :) Il utilise la formule correcte, évite math.factorial et nécessite moins d'opérations de multiplication:

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)

De plus, pour éviter les arithmétiques de grand entier, vous pouvez utiliser des nombres en virgule flottante, convertir product(a[i])/product(b[i]) en product(a[i]/b[i]) et réécrire le programme ci-dessus de la manière suivante:

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

Je recommande d'utiliser la programmation dynamique (DP) pour calculer les coefficients binomiaux. Contrairement au calcul direct, il évite la multiplication et la division de grands nombres. En plus de la solution récursive, il stocke dans une table les sous-problèmes qui se chevauchent et se chevauchent pour une recherche rapide. Le code ci-dessous montre les implémentations DP ascendantes (tabulaires) et DP descendantes (mémoisées) pour le calcul des coefficients binomiaux. 

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

Remarque: la taille de la table des notes est définie sur une petite valeur (6) à des fins d'affichage. Elle doit être augmentée si vous calculez des coefficients binomiaux pour les grands n et k.

0
Vadim Smolyakov

C'est une bonne idée d'appliquer une définition récursive, comme dans la réponse de Vadim Smolyakov, associée à un DP (programmation dynamique), mais pour ce dernier, vous pouvez appliquer le décorateur lru_cache du module 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)
0
Jos Vrancken