根据维基百科上的介绍,排列(permutation)是将相异物体根据确定的顺序重排,每一种顺序都是一个排列。而组合(combination)较排列的区别是,组合不考虑排序顺序,当两个集合中元素相等,则视为同一种方式。
计算方式
排列:
P
=
A
n
m
=
n
!
(
n
−
m
)
!
P = A_n^m = \frac{n!}{(n-m)!}
P=Anm=(n−m)!n!
组合:
P
=
C
n
m
=
n
!
m
!
(
n
−
m
)
!
P = C_n^m = \frac{n!}{m!(n-m)!}
P=Cnm=m!(n−m)!n!
应用
比赛赛程排列:如田忌赛马。
自然语言处理文字清洗。
C++代码实现排列
/** permutaion: 排列。
* 从n个数中选出m个数的方式,若不考虑顺序Cn(m),若考虑顺序An(m)
*//* 问题:密码破解
* 假设有一个 4 位字母密码,每位密码是 a~e 之间的小写字。
* 编写代码暴力破解密码。
* 提示:根据可重复排列的规律,生成所有可能的 4 位密码。
*/
#include <iostream>
#include <vector>
using namespace std;
class Permutation {
private:
int resultCount_ = 0;
public:
/** Details: 根据输入字母列表,获得所有的排列方式。
* params: result- 当前排列形式, candidate- 未排列字母表。
* return: null
*/
void breakPassword(vector<string> result, vector<string> candidate) {
int len = candidate.size();
if (0 == len) {
// 无字母剩余,输出排列结果
outputResult(result);
resultCount_++;
return;
}
for (int i = 0; i < len; i++) {
vector<string> resultNew;
vector<string> candidateLeft;
// 读取排列字母
resultNew = result;
resultNew.push_back(candidate[i]);
// 获得剩余字母表
candidateLeft = candidate;
vector<string>::iterator it = candidateLeft.begin();
candidateLeft.erase(it + i);
// 递归
breakPassword(resultNew, candidateLeft);
}
}
// 输出结果
void outputResult(vector<string> result) {
for (unsigned int i = 0; i < result.size(); i++) {
cout << result[i];
}
cout << endl;
}
// 获得所有可能密码总数
int getResultCount() {
return resultCount_;
}
};
int main(void) {
vector<string> fourAlphabetString = {"a", "b", "c", "d", "e"};
vector<string> res;
Permutation test;
test.breakPassword(res, fourAlphabetString);
cout << "可能的密码形式:";
cout << test.getResultCount() << "种" << endl;
}
C++代码实现组合
#include <cmath>
#include <iostream>
#include <vector>
using namespace std;
// 组合
// 思想:从一些对象n中选出一部分m(1<=m<=n),不考虑顺序,可能的形式有几种。
// 应用:队伍参赛排序。自然语言处理中,处理输入词。当输入词相同仅顺序不一致时,
// 可以采用组合的概念将其排序,并认定为意思相同。
template<typename T> class Combination {
private:
const int firstPrize;
const int secondPrize;
const int thirdPrize;
public:
Combination(int x, int y, int z)
: firstPrize(x), secondPrize(y), thirdPrize(z) {
}
/**
* @description:
* 从10个人中选出三等奖3人,二等奖2人,一等奖1人,不能重复获奖。
* @param {type} rewardNum- 指定赏金数, result- 奖赏方式结果。
* @return: null
*/
void rewardMethods(vector<T> result, vector<T> candidate){
unsigned int len = thirdPrize + secondPrize + firstPrize;
if (result.size() == len) {
// 输出结果
resultOutput(result);
return;
} else {
for (unsigned int i = 0; i < candidate.size(); i++) {
vector<int> resultNew = result;
resultNew.push_back(candidate[i]);
vector<int> candidateNew;
copyElem(candidate, candidateNew, i + 1);
rewardMethods(resultNew, candidateNew);
}
}
}
// 数据复制函数
void copyElem(vector<T>& input, vector<T>& output, int i){
vector<int>::iterator it = input.begin() + i;
for (; it < input.end(); it++) {
output.push_back(*it);
}
}
// 输出结果
void resultOutput(vector<T> res) {
for (unsigned int i = 0; i < res.size(); i++) {
if (i == thirdPrize) cout << '*';
if (i == thirdPrize + secondPrize)
cout << '*';
cout << res[i] << ' ';
}
cout << endl;
}
};
// test
int main(void) {
Combination <int> test(1, 2, 3);
vector<int> res; vector<int> candidate;
// 输入
for (unsigned int i = 0; i < 10; i++) {
candidate.push_back(i + 1);
}
test.rewardMethods(res, candidate);
}
更多内容,可关注公众号:青椒小记