九、Python编程解决组合问题(之一…

本文介绍了使用Python编程解决组合问题的方法,包括简单的循环算法和递归算法。通过具体实例展示了如何列出所有可能的组合,并提供了详细的代码解释。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

九、Python编程解决组合问题(之一)

----From a high school student's view to learn Python

关键字:

python 组合问题 编程 递归 函数 元组 堆栈 列表 combination function recursive tuple list stack

 

一、组合问题概述

组合是指从给定个数的元素中仅仅取出指定个数的元素,不考虑排序。组合的公式如下:

从m个元素中取n个元素的组合记为:C(m, n),则:

C(m, n) = m!/((m-n)!n!)

在知道了组合的定义以及公式之后,我们要求比如9个数中取4个数的组合以及列出各种组合,是非常简单的,C(9, 4)=126。

我开始也想当然,用计算机来解决组合问题,那是不是太小儿科了,可当我一开始着手做,才知道这还真不是一个简单的问题。当然对于计算组合的数量是非常简单(其实,对于我们刚开始学习编程,写一个计算组合数量的程序也不算很简单),那复杂在什么地方呢?

对于C(9, 4),复杂在于我们如何将所有的组合列出,我们先拿6个数来举例。

列出1 2 3 4 5 6六个数的所有任意四个数的组合

1    固定1 2 3,组合有:1234 1235 1236

2    固定1 2 4,组合有:1245 1246

3    固定1 2 5,组合有:1256

4    固定1 3 4,组合有:1345 1346

5    固定1 3 5,组合有:1356

6    固定1 4 5,组合有:1456

7    固定2 3 4,组合有:2345 2346

8    固定2 3 5,组合有:2356

9    固定2 4 5,组合有:2456

10   固定3 4 5,组合有:3456

共计15种,与C(6, 4)=6*5*4*3/(1*2*3*4)=15相符。

二、组合问题的简单算法

如何把这些步骤进行归纳总结,形成一个算法,通过编程让计算机可以自动完成列出各种组合的任务呢?这下你应该体会到,计算机其实并不“聪明”,如果没有人给它执行指令,它其实啥也不会做。言归正传,我们还是来分析一下,这个问题如何解决:

首先,我们分析上面列出的10个步骤,对于1),很明显,在固定了1 2 3这三个数之后,第四个数就是4、5、6三个数进行了循环(还记得循环吗,计算机最拿手的就是做循环)。

其次,我们再分析1)2)3)这三个步骤中固定的数字,前两个没有变,都是1 2,只是第三个数在进行循环(又是循环)。

 

通过分析,我们可以找到这样的规律:

1    先固定3个数,让第四个数进行循环

2    让第三个数循环,重复第一步

3    让第二个数循环,重复第一步

4    让第一个数循环,重复第一步

似乎几个循环就能够搞定,确实如此,看看下面的程序:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

def combNumberLoop4(m, b):

    totalNumber = 0

    for i in range(1, m+2-4):

        b[0] = i

        for j in range(i+1, m+2-3):

            b[1] = j

            for k in range(j+1, m+2-2):

                b[2] = k

                for l in range(k+1, m+2-1):

                    b[3] = l

                    print b

                    totalNumber += 1

    return totalNumber

 

group=[99,99,99,99]

print "\nUsing Loop: %d\n" % combNumberLoop4(6, group)

 

 

程序的运行结果如下:

[1, 2, 3, 4]

[1, 2, 3, 5]

[1, 2, 3, 6]

[1, 2, 4, 5]

[1, 2, 4, 6]

[1, 2, 5, 6]

[1, 3, 4, 5]

[1, 3, 4, 6]

[1, 3, 5, 6]

[1, 4, 5, 6]

[2, 3, 4, 5]

[2, 3, 4, 6]

[2, 3, 5, 6]

[2, 4, 5, 6]

[3, 4, 5, 6]

 

Using Loop: 15

 

对这个程序,做一些说明:

程序使用了函数,函数名为combNumberLoop4

l程序使用了循环,而且是一个多重循环,注意:循环的缩进,循环的变量,循环的范围;每一层的循环,range的起始值都依赖于上一层的循环变量,如果对range函数还有疑问的,请复习一下。

l程序设置了一个计数的变量totalNumber,每算出一种组合,变量+1;

l程序有一个return语句,用来返回总共有多少种组合;

l程序中使用了列表(list)数据类型,我们先初始化了一个有四个元素的list变量group,group在程序中作为参数,传入到函数中使用,在函数的定义中,我们使用b来接收group,所以在函数中,对于b的操作其实就是对group的操作。函数中有对list中单个元素进行更新;

l对于print语句的使用,我们使用了格式化输出,用来显示计算列出的组合数量;

l对于函数的调用,我们直接把调用放在了print语句中,显示了python的灵活语法规则;

l再一次的强调“缩进”“:”,特别是“缩进”,经常会引起莫名其妙的问题,教训深刻;

 

我们把变量group在调用函数时,作为参数在传入,那再函数调用返回之后,group的值改变了吗?请大家自己试一试。

 

组合问题似乎就这么顺利的解决了,但恐怕没有这么简单:

你有没有发现,这个函数有一个严重的缺陷(思考一下),它只能够解决四个数的组合问题。

 

如果你想解决非四个数的组合问题,那你还得再写一个函数,可是在程序中,为了解决通用型的问题,类似C(m, n),m、n都会是变化的,我们总不能写n个函数来实现吧?我们似乎应该找一个更通用的办法。

三、组合问题的递归解决

我们再回到C(6,4)问题,除了上面列出的十个步骤,我们换个思路:

如果在6个数中选定一个数,那么确定剩下的3个数是不是变为了“从5个数中选3个数的组合问题”了呢?以此类推,这似乎变成了一个“递归”问题了,确实,我们可以用递归的思路来解决这个问题。

 

对于C(m, n)列出所有组合的问题,可以按照一下的步骤来进行编程,这次我们按从后往前的顺序来列出所有组合:

1   选定一个元素i,在范围[m, n]内进行循环

2   将i 作为所求组合的最后一个元素

3   如果n-1>0,进行递归调用,求C(m-1, n-1);否则,输出结果

4   重复①

注意,设计递归函数一定要有终止条件,否则会造成“死循环”。

实现的代码如下:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

def combNumber(m, n, b):

    global totalNumberR

    for i in range(m, n-1, -1):   

        b[n-1] = i

        if n-1>0:

            combNumber(i-1,n-1,b)

        else:

            print b

            totalNumberR += 1

    return totalNumberR

 

group=[99,99,99,99,99]

totalNumberR = 0

print "\nUsing Recursive: %d\n" % combNumber(7,5,group)

 

 

运行结果:

[3, 4, 5, 6, 7]

[2, 4, 5, 6, 7]

[1, 4, 5, 6, 7]

[2, 3, 5, 6, 7]

[1, 3, 5, 6, 7]

[1, 2, 5, 6, 7]

[2, 3, 4, 6, 7]

[1, 3, 4, 6, 7]

[1, 2, 4, 6, 7]

[1, 2, 3, 6, 7]

[2, 3, 4, 5, 7]

[1, 3, 4, 5, 7]

[1, 2, 4, 5, 7]

[1, 2, 3, 5, 7]

[1, 2, 3, 4, 7]

[2, 3, 4, 5, 6]

[1, 3, 4, 5, 6]

[1, 2, 4, 5, 6]

[1, 2, 3, 5, 6]

[1, 2, 3, 4, 6]

[1, 2, 3, 4, 5]

 

Using Recursive: 21

我们还是按照之前的方式对程序进行一些解释:

l  这是一个通用的程序,这个程序没有前一个程序的限制,可以解决从1~m的数中取n个数的组合问题

l  为了返回正确的组合数量的统计值,我们设置了变量totalNumberR,注意该变量我们在函数中将它使用global强制转为全局变量,大家可以试一试,如果不这样做,看看返回的结果是什么

l  从执行结果来看,这次的组合与之前的例子不同,是从最后面的一组组合开始的,在函数的循环中,range使用的步进值为-1

l  因为要求5个元素的组合,我们将group初始化为5个元素的list

l  注意递归调用的参数

 

递归的使用,让程序写起来非常的简洁,但是,在写程序的时候,我有两个地方(其实程序也就难在这两个地方),折腾了好几天才弄清楚:

l   首先是for循环的循环范围如何确定;

l   其次是递归调用时使用的参数设计;

 

和之前介绍递归函数时使用的斐波那契数列的例子,这个程序更难理解执行的顺序,不象fib,只是层层调用而已,而这个程序在调用外面还套了一层循环,增加了理解的难度。

BasicBasic BasicBasic正是微软公司售出的第一套件。 正是微软公司售出的第一套件。 Basic Basic Basic Basic 发展到今天已经有很多版本,如 发展到今天已经有很多版本,如 发展到今天已经有很多版本,如 发展到今天已经有很多版本,如 发展到今天已经有很多版本,如 发展到今天已经有很多版本,如 发展到今天已经有很多版本,如 发展到今天已经有很多版本,如 发展到今天已经有很多版本,如 发展到今天已经有很多版本,如 发展到今天已经有很多版本,如 发展到今天已经有很多版本,如 发展到今天已经有很多版本,如 发展到今天已经有很多版本,如 GW -Basic Basic Basic Basic 、 QuickBasic QuickBasic QuickBasic QuickBasic QuickBasic QuickBasic QuickBasic 、QBasic QBasic QBasic QBasic QBasic 、Visual Basic Visual Basic Visual Basic Visual Basic Visual Basic Visual Basic Visual Basic Visual Basic , 等其中Visual Basic Visual Basic Visual Basic Visual Basic Visual Basic Visual Basic Visual Basic Visual Basic Visual Basic 是最容易学习与应用的程序语 是最容易学习与应用的程序语 是最容易学习与应用的程序语 是最容易学习与应用的程序语 是最容易学习与应用的程序语 是最容易学习与应用的程序语 是最容易学习与应用的程序语 言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来言之一。 虽然最容易学习与使用,但它的功能却非常强大很多应程序都是编写出来不是个都能成为比尔盖茨,但你想知道天使用的 不是个都能成为比尔盖茨,但你想知道天使用的 不是个都能成为比尔盖茨,但你想知道天使用的 不是个都能成为比尔盖茨,但你想知道天使用的 不是个都能成为比尔盖茨,但你想知道天使用的 不是个都能成为比尔盖茨,但你想知道天使用的 不是个都能成为比尔盖茨,但你想知道天使用的 不是个都能成为比尔盖茨,但你想知道天使用的 不是个都能成为比尔盖茨,但你想知道天使用的 不是个都能成为比尔盖茨,但你想知道天使用的 不是个都能成为比尔盖茨,但你想知道天使用的 不是个都能成为比尔盖茨,但你想知道天使用的 WindowsWindowsWindows Windows的诸多功能是如何实现吗? 的诸多功能是如何实现吗? 的诸多功能是如何实现吗? 的诸多功能是如何实现吗? 的诸多功能是如何实现吗? 的诸多功能是如何实现吗? 的诸多功能是如何实现吗? 你想要编写自己的应用程 你想要编写自己的应用程 你想要编写自己的应用程 你想要编写自己的应用程 你想要编写自己的应用程 序吗?通过学习 序吗?通过学习 序吗?通过学习 序吗?通过学习 VB 就能写出很多应用程序。 就能写出很多应用程序。 就能写出很多应用程序。 就能写出很多应用程序。 就
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值