组合数学 (二): 排列组合

本文介绍了组合数学中的排列和组合概念,提供了相关的计算公式,并通过代码示例展示了如何输出所有排列和组合。在排列部分,解释了从M个数中取出N个数的排列数计算方式;在组合部分,阐述了不计顺序的组合数计算方法。此外,还详细说明了如何使用递归生成排列和错位排列,以及如何通过递归实现任意大小的组合输出。

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

公式

排列

从 M 个数中有序地拿出 N 个数的方案数为

P M N = M ! ( M − N ) ! P_M^N=\frac{M!}{(M-N)!} PMN=(MN)!M!

简单解释就是: M 个数的全排列个数为 M ! M! M!, 如果拿出 N 个数, 那么就要去掉多出来的所有排列, 所以就要除以 ( M − N ) ! (M-N)! (MN)!

组合

从 M 个数中不计顺序地拿出 N 个数的方案数为

C M N = M ! N ! ( M − N ) ! C_M^N=\frac{M!}{N!(M-N)!} CMN=N!(MN)!M!

我认为也比较好理解, 在相同 M, N 的条件下, 组合就是不计顺序的排列, 所以组合的个数就是排列的个数除以排列的方案, 因此也就是 C M N = P M N N ! C_M^N=\frac{P_M^N}{N!} CMN=N!PMN

代码

输出所有排列

给定 M 和 N, 输出一个长为 M 的数组的所有排列, 可以用递归的方式解决:
递归一共有 N 层, 第 i 递归会选择第 i 个位置的输出, 显然如果某个位置在之前已经被选过了, 那么它就不能再被选.
C 代码如下

#include <stdio.h>
#define MAXN 100

// 从 M 个数中选出 N 个数来排列
int M, N;

// 用来存放数据
int array[MAXN];

// 用来缓存输出
int buffer[MAXN];

// 用来表示这个位置是否已经被用过
int flag[MAXN];

// 输出缓存中的 N 个数
void out() {
	for (int i = 0; i < N; i++) {
		printf("%d ", buffer[i]);
	}
	printf("\n");
}

// 递归进行排列, 其中 pos 表示当前要选择的位置
void perm(int pos) {
	for (int i = 0; i < M; i++) {
		// 如果这个位置已经被标记过, 就 continue, 否则标记这个位置
		if (flag[i]) continue;
		flag[i] = 1;
		buffer[pos] = array[i];
		// 如果到底了, 就输出, 否则进行下一层递归
		if (pos == (N-1)) {
			out();
		} else {
			perm(pos+1);
		}
		// 释放标记
		flag[i] = 0;
	}
}

int main() {
	scanf("%d %d" &M, &N);
	for (int i = 0; i < M; i++) {
		scanf("%d", array + i);
	}
	perm(0);
}

错位排列

错位排列和上面的区别是, 排列的第 i 个数字不能是原本的第 i 个数字.
只需要在上面的代码中再加一行代码就行了

#include <stdio.h>
#define MAXM 100

int M, N;
int array[MAXM];
int buffer[MAXM];
int flag[MAXM];

void perm(int pos) {
	for (int i = 0; i < M; i++) {
		if (flag[i]) continue;
		// 加下面这一行
		if (i == pos) continue;
		flag[i] = 1;
		buffer[pos] = array[i];

		if (pos == (N - 1)) {
			for (int j = 0; j < N; j++) {
				printf("%d ", buffer[j]);
			}
			printf("\n");
		} else {
			perm(pos + 1);
		}

		flag[i] = 0;
	}
}

int main() {
	scanf("%d %d" &M, &N);
	for (int i = 0; i < M; i++) {
		scanf("%d", array + i);
	}
	perm(0);
}

输出所有组合

从 M 个数中选取 N 个数, 假如我们知道 N, 比如 N = 2, 那么我们可以写一个二层 for 循环:

for (int i = 0; i < M; i++) {
	for (int j = i+1; j < M; j++) {
	}
}

即可输出所有组合.
因此, 我们可以用递归的方式来做到对任意的 N 进行组合:

#include <stdio.h>
#define MAXM 100

int M, N;
int array[MAXM];
int buffer[MAXM];

void combo(int pos, int from) {
	// 从 from 开始遍历, from 的值是上一层递归的 i + 1
	for (int i = from; i < M; i++) {
		buffer[pos] = array[i];
		// 如果递归到底了, 输出, 否则下一层递归
		if (pos == (N - 1)) {
			for (int j = 0; j < N; j++) {
				printf("%d ", buffer[j]);
			}
			printf("\n");
		} else {
			combo(pos + 1, i + 1);
		}
	}
}

int main() {
	scanf("%d %d" &M, &N);
	for (int i = 0; i < M; i++) {
		scanf("%d", array + i);
	}
	combo(0, 0);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值