Python算法:数的全排列、组合详解

目录

求n个数的全排列

使用递归算法求全排列(暴力法)

代码实现

求n个数中随机m个数的全排列

求n个数中任意m个数的组合

二进制法求子集代码:

代码实现


求n个数的全排列

我们假设要求十个数的全排列,我们有一种十分简单的方法,写一个10级的for循环,如下

for i in range(1,11):
	for j in range(1,11):  #并且让j不等于i
		for k in range(1,11):  #并且让k不等于i,j
			......

这是一种非常容易想到的方法,但我相信没有人会喜欢这种方法。

接下来我们使用另一种方法;

使用递归算法求全排列(暴力法)

求 {1  2  3  4  5......n}的全排列的思路如下:

(1)让第一个数不同,得到n个数列(办法是:把第1个和后面每个数交换即可):

1 2 3 4 5......n

2 1 3 4 5......n

.....

n 2 3 4 5......1      

以上n个数列,只要第一个数不同,不管后面n-1个数是怎么排列的,这n个数列都不同。

这是递归的第一层

(2)继续:在上面的每个数列中,去掉第一个数,对后面的n-1个数进行类似的排列。例如从上面第2行的{2 1 3 4 5......n}进入第二层(去掉首位2):

1 3 4 5......n

3 1 4 5......n      

 ......        

n 3 4 5......1      

以上n-1个数列,只要第一个数不同,不管后面n-2个数是怎么排列的,这n-1个数列都不同。

这是递归的第二层。

(3)继续以上过程,直到用完所有数字。

而我们不难得出,这个算法总共有n×(n-1)×(n-2)×......×1个情况。

代码实现

def swap(s,i,j):
    temp = s[i]
    s[i] = s[j]
    s[j] = temp
#全排列实现
def perm(begin,end):
    global num
    if begin == end:
        print(nums)
    else:
        for i in range(begin,end+1):
            swap(nums,begin,i)
            perm(begin+1,end)
            swap(nums,begin,i)
num = 0
nums = [1,2,3,4,5,6,7,8,9,10]
perm(0,9)

如果想要计算次数,则只需将每一个排列结束时的print语句改成num+=1即可

求n个数中随机m个数的全排列

例如在10个数中取任意3个数的全排列,在递归程序中只修改一个地方就可以了:

if begin == 3:         #将end改为3即可
    print(nums[:3])

求n个数中任意m个数的组合

想要求n个数中任意m个数的组合,我们首先得要知道如何使用二进制法求子集。

例如n=3的集合{a0, a1, a2},它的子集和二进制数的对应关系是:

 每个子集对应一个二进制数; 这个二进制数中的每个1,都对应了这个子集中的某个元素。

子集的数量是2 ** n个,因为所有二进制数的总个数是2 ** n。

二进制法求子集代码:

def print_subset(n):
    for i in range(1,1<<n):
        for j in range(n):
            if (i & 1<<j):
                print(j,end=' ')
        print()
while True:
    n = int(input())
    print_subset(n)

然后我们在回到我们想要求的问题, 求n个数中任意m个数的组合,对照子集生成的二进制方法,很容易看出,在n个元素的集合中找k个元素的子集,这个子集对应了1的个数为k的二进制数。

想要求出m个数的组合,那我们只需判断我们的k是否等于m便可

如何判断二进制数中1的个数为k个?

一个神奇的操作:kk= kk & (kk - 1),它能消除kk的二进制数的最后一个1。

例如:7,二进制是111     111&(111-1) = 111&110 = 110

利用这个操作,可以计算出二进制数中1的个数。

例如:7,二进制是111     

111&(111-1) = 111&110 = 110     

110&(110-1) =110&101 =100     

100&(100-1) =100&011 =000    

所以有3个1

代码实现

def print_subset(n,k):
    for i in range(1,1<<n):
        num,kk = 0,i
        while kk:
            # 例如:7,二进制是111
            # 111 & (111 - 1) = 111 & 110 = 110
            # 110 & (110 - 1) = 110 & 101 = 100
            # 100 & (100 - 1) = 100 & 011 = 000
            kk = kk & (kk-1)
            num += 1
        if num == k:
            for j in range(n):
                if (i & 1<<j):
                    print(j,end=' ')
            print()
while True:
    n,k = map(int,input().split())
    print_subset(n,k)

  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值