如标题所述,作者认为把一个正整数拆分成几个正整数的和很重要。你可能会疑惑,很重要吗?好像不很重要,因为似乎没啥实际的用处,无论是学习还是工作,没有遇到过这种需求,但其实它非常重要,如果你曾经遇到过一个问题,n个结点的树,它有多少种形态,这个问题数据结构与算法的书里有答案,但如果要你画出所有树的形态,你画得出来吗?或者说,你有思路吗?
让我们思考一下,n个结点的树的情形。首先它有一个根结点,然后它有几棵子树,显然它的各个子树的结点数之和是n-1,如果问它的几棵子树各有多少个结点?这不就是把n-1拆分成几个正整数的和的问题吗?所以,如果你知道如何拆分一个正整数,你就能知道n个结点的树,每一种形态下,它的各个子树各有多少个结点,这些都知道了,写一个程序画出n个结点的树的所有形态就不难了。作者之所以认为它很重要,是因为,树在编程里是非常重要的数据结构,对它没有一个形象立体的认识,是不利于你去真正得学习掌握它。
下面让我们来研究一下如何拆分一个正整数,举例来说,对3进行拆分,很容易给出所有拆分的情形,3=1+1+1,3=1+2,3=2+1,3=3,为了简化输出,我们把3=1+2和3=2+1看成同一种拆分,我们以3=1+2代表了,也就是设定拆分序列为正序。那么如何进行拆分呢?其实是很简单的,这里存在一个递归的规律,只要找到了这个规律,问题就迎刃而解。对n进行拆分,假设拆出来的第一个数是m,那么对n的拆分便分解为了拆出一个数m和对n-m的拆分的问题了,数m可取哪些值?按照假定拆分序列为正序,m只能由上一步拆分的值m’(如果m是第一步拆出来的值m由1开始取)开始取直到 n/2(下取整),如果不假定拆分序列为正序,m更简单由1取到n就可以了,这里给出一个拆分为正序序列的实现,下面是代码。
class Intization(object):
def __call__(self, n):
self.n = n
self.res = []
yield from self.intization(self.n, 1)
def intization(self, n, frm):
for i in range(frm, int(n / 2) + 1):
self.res.append(i)
yield from self.intization(n - i, i)
self.res.pop()
#n不能被拆分就走到这里,意味着n就是拆分序列最后一个数
self.res.append(n)
yield self.res
self.res.pop()
_i = Intization()
for i in _i(5):
print(i)
上面的实现需要定义一个类,稍显繁琐,不需要定义类的版本如下:
def intization(n):
res = []
def takeapart(n, frm):
for i in range(frm, int(n / 2) + 1):
res.append(i)
yield from takeapart(n - i, i)
res.pop()
res.append(n)
yield res
res.pop()
yield from takeapart(n, 1)
for i in intization(5):
print(i)