web-dev-qa-db-fra.com

Comment fonctionne la fonction de réduction?

Autant que je sache, la fonction de réduction prend une liste l et une fonction f. Ensuite, il appelle la fonction f sur les deux premiers éléments de la liste, puis appelle à plusieurs reprises la fonction f avec l'élément de liste suivant et le résultat précédent. 

Donc, je définis les fonctions suivantes:

La fonction suivante calcule la factorielle. 

def fact(n):
    if n == 0 or n == 1:
        return 1
    return fact(n-1) * n


def reduce_func(x,y):
    return fact(x) * fact(y)

lst = [1, 3, 1]
print reduce(reduce_func, lst)

Cela ne devrait-il pas me donner ((1! * 3!) * 1!) = 6? Mais au lieu de cela, il donne 720. Pourquoi 720? Il semble prendre aussi la factorielle de 6. Mais j'ai besoin de comprendre pourquoi.

Quelqu'un peut-il expliquer pourquoi cela se produit et une solution de contournement?

En gros, je souhaite calculer le produit des factorielles de toutes les entrées de la liste . Le plan de sauvegarde consiste à exécuter une boucle et à la calculer. Mais, je préférerais utiliser réduire.

33
Divya

Ok, compris

Je dois d'abord mapper les nombres sur leurs factorielles, puis appeler réducteur avec multiplicateur. 

Donc, cela fonctionnerait:

lst_fact = map(fact, lst)
reduce(operator.mul, lst_fact)
1
Divya

Les autres réponses sont géniales. J'ajouterai simplement un exemple illustré qui me semble très utile pour comprendre reduce():

>>> reduce(lambda x,y: x+y, [47,11,42,13])
113

sera calculé comme suit:

 enter image description here

( Source ) ( miroir )

62
Franck Dernoncourt

Le moyen le plus simple de comprendre reduction () est de regarder son code pur équivalent en Python:

def myreduce(func, iterable, start=None):
    it = iter(iterable)
    if start is None:
        try:
            start = next(it)
        except StopIteration:
            raise TypeError('reduce() of empty sequence with no initial value')
    accum_value = start
    for x in iterable:
        accum_value = func(accum_value, x)
    return accum_value

Vous pouvez voir que cela n’a de sens que pour reduction_func () d’appliquer la factorielle à l’argument le plus à droite:

def fact(n):
    if n == 0 or n == 1:
        return 1
    return fact(n-1) * n

def reduce_func(x,y):
    return x * fact(y)

lst = [1, 3, 1]
print reduce(reduce_func, lst)

Avec cette petite révision, le code produit 6 comme vous le souhaitiez :-)

29
Raymond Hettinger

Votre fonction appelle fact() sur les deux arguments . Vous calculez ((1! * 3!)! * 1!). La solution de contournement consiste à l'appeler uniquement sur le deuxième argument et à transmettre à reduce() une valeur initiale de 1.

9

De la documentation Python reduce ,

réduire (fonction, séquence) renvoie une valeur unique construite en appelant la fonction (binaire) sur les deux premiers éléments de la séquence, puis sur le résultat et sur l'élément suivant, etc.

Alors, traversant. Il calcule reduce_func des deux premiers éléments, reduce_func(1, 3) = 1! * 3! = 6. Ensuite, il calcule reduce_func du résultat et l'élément suivant: reduce_func(6, 1) = 6! * 1! = 720.

Vous avez raté cela. Lorsque le résultat du premier appel reduce_func est transmis en entrée au second, il est factorisé avant la multiplication.

8
Brooks Moses

Tout d’abord, votre reduce_func n’a pas la structure d’un pli; cela ne correspond pas à votre description d'un pli (ce qui est correct).

La structure d'un pli est la suivante: def foldl(func, start, iter): return func(start, foldl(func, next(iter), iter)

Maintenant, votre fonction fact ne fonctionne pas sur deux éléments - elle calcule simplement factorielle.

Donc, en résumé, vous n'utilisez pas un pli et avec cette définition de factorielle, vous n'en avez pas besoin.

Si vous voulez jouer avec factorial, jetez un œil au combinateur y: http://mvanier.livejournal.com/2897.html

Si vous voulez en savoir plus sur les plis, regardez ma réponse à cette question, qui montre son utilisation pour calculer des fractions cumulatives: création d'un pourcentage cumulatif à partir d'un dictionnaire de données

0
Marcin

Vous pouvez également implémenter factorielle en utilisant réduire.

def factorial(n):
  return(reduce(lambda x,y:x*y,range(n+1)[1:]))
0
passmaster10

Réduire exécute la fonction du paramètre n ° 1 successivement à l'aide des valeurs fournies par l'itérateur dans le paramètre n ° 2. 

print '-------------- Example: Reduce(x + y) --------------'

def add(x,y): return x+y
x = 5
y = 10

import functools
tot = functools.reduce(add, range(5, 10))
print 'reduce('+str(x)+','+str(y)+')=' ,tot

def myreduce(a,b):
    tot = 0
    for i in range(a,b):
        tot = tot+i
        print i,tot
    print 'myreduce('+str(a)+','+str(b)+')=' ,tot

myreduce(x,y)

print '-------------- Example: Reduce(x * y) --------------'

def add(x,y): return x*y
x = 5
y = 10

import functools
tot = functools.reduce(add, range(5, 10))
print 'reduce('+str(x)+','+str(y)+')=' ,tot

def myreduce(a,b):
    tot = 1
    for i in range(a,b):
        tot = tot * i
        print i,tot
    print 'myreduce('+str(a)+','+str(b)+')=' ,tot

myreduce(x,y)
0
Justin Malinchak