在lambda函数中使用math.factorial和reduce()
我正在尝试写一个函数,用来计算一个字符串的不同排列组合的数量。例如,字符串 aaa
应该返回 1
,而 abc
应该返回 6
。
我写这个方法是这样的:
(伪代码:)
len(string)! / (A!*B!*C!*...)
这里的 A、B、C 是每个独特字符出现的次数。比如,字符串 'aaa'
的计算方式是 3! / 3! = 1
,而 'abc'
的计算方式是 3! / (1! * 1! * 1!) = 6
。
到目前为止,我的代码是这样的:
def permutations(n):
'''
returns the number of UNIQUE permutations of n
'''
from math import factorial
lst = []
n = str(n)
for l in set(n):
lst.append(n.count(l))
return factorial(len(n)) / reduce(lambda x,y: factorial(x) * factorial(y), lst)
一切都运行得很好,除了当我尝试传入一个只有一个独特字符的字符串,比如 aaa
时,我得到了错误的答案:
>>> perm('abc')
6
>>> perm('aaa')
2
>>> perm('aaaa')
6
现在,我知道问题出在用阶乘计算的 lambda 函数在长度为 1 的列表上运行时。我不太明白为什么。不过,大多数其他的 lambda 函数在长度为 1 的列表上也能正常工作,即使它们期望有两个元素:
>>> reduce(lambda x,y: x * y, [3])
3
>>> reduce(lambda x,y: x + y, [3])
3
但这个就不行:
>>> reduce(lambda x,y: ord(x) + ord(y), ['a'])
'a'
>>> reduce(lambda x,y: ord(x) + ord(y), ['a','b'])
195
我是不是应该以不同的方式来处理这个问题?我知道我可以用很多不同的方法重写这个函数来避免这个问题(例如,不使用 lambda
),但我想知道为什么这个特定的情况不工作。
4 个回答
如果你想要计算 len(s)! / A!*B!*C!
,那么使用 reduce()
是不行的,因为它会计算 factorial(factorial(A)*factorial(B))*factorial(C)
。换句话说,这个操作需要是可交换的,也就是顺序不影响结果。
相反,你需要先生成一个阶乘的列表,然后把它们相乘起来:
import operator
reduce(operator.mul, [factorial(x) for x in lst])
查看关于 reduce()
的文档,里面有一个可选的 'initializer' 参数,它会放在列表中所有其他元素之前,这样对于只有一个元素的列表,行为就会保持一致。例如,对于你的 ord()
函数,可以把 initializer
设置为一个字符,它的 ord()
值是 0:
>>> reduce(lambda x, y: ord(x) + ord(y), ['a'], chr(0))
97
Python的reduce
函数有时候不知道默认的初始值应该是什么。应该有一个版本可以让你自己指定初始值。只要提供一个合适的初始值,你的reduce
就能很好地工作。
另外,从评论来看,你可能只需要在你的lambda表达式的第二个参数上使用factorial
函数:
reduce(lambda x,y: x * factorial(y), lst, 1)