Created
July 23, 2025 08:35
-
-
Save Wxh16144/b151496c1d6249967efe5c167b9ac371 to your computer and use it in GitHub Desktop.
CombinatoricsKit 排列、组合、全排列与笛卡尔积工具类 (TypeScript)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * 集合操作工具类 | |
| * 支持排列、组合、笛卡尔积和全排列操作 | |
| */ | |
| class CombinatoricsKit<T> { | |
| private elements: T[]; | |
| // #region 构造函数 | |
| /** | |
| * 构造函数 | |
| * @param elements 集合元素数组 | |
| */ | |
| constructor(elements: T[] = []) { | |
| // 使用副本防止外部修改 | |
| this.elements = [...elements]; | |
| } | |
| // #endregion | |
| // #region 排列相关方法 | |
| /** | |
| * 计算排列数 P(n,m) = n!/(n-m)! | |
| * @param m 选取的元素数量 | |
| * @returns 排列数 | |
| */ | |
| permutationCount(m: number): number { | |
| if (m < 0 || m > this.elements.length) { | |
| throw new Error(`Invalid value for m: ${m}. Must be between 0 and ${this.elements.length}.`); | |
| } | |
| return this.factorial(this.elements.length) / this.factorial(this.elements.length - m); | |
| } | |
| /** | |
| * 生成所有可能的排列(从n个元素中取m个并考虑顺序) | |
| * @param m 选取的元素数量 | |
| * @returns 所有可能排列的数组 | |
| */ | |
| permutations(m: number): T[][] { | |
| if (m < 0 || m > this.elements.length) { | |
| throw new Error(`Invalid value for m: ${m}. Must be between 0 and ${this.elements.length}.`); | |
| } | |
| const result: T[][] = []; | |
| // 辅助函数:生成排列 | |
| const generatePermutation = (arr: T[], current: T[] = []): void => { | |
| if (current.length === m) { | |
| result.push([...current]); | |
| return; | |
| } | |
| for (let i = 0; i < arr.length; i++) { | |
| const newArr = [...arr.slice(0, i), ...arr.slice(i + 1)]; | |
| current.push(arr[i]); | |
| generatePermutation(newArr, current); | |
| current.pop(); | |
| } | |
| }; | |
| generatePermutation([...this.elements]); | |
| return result; | |
| } | |
| // #endregion | |
| // #region 组合相关方法 | |
| /** | |
| * 计算组合数 C(n,m) = n!/(m!(n-m)!) | |
| * @param m 选取的元素数量 | |
| * @returns 组合数 | |
| */ | |
| combinationCount(m: number): number { | |
| if (m < 0 || m > this.elements.length) { | |
| throw new Error(`Invalid value for m: ${m}. Must be between 0 and ${this.elements.length}.`); | |
| } | |
| return this.factorial(this.elements.length) / | |
| (this.factorial(m) * this.factorial(this.elements.length - m)); | |
| } | |
| /** | |
| * 生成所有可能的组合(从n个元素中取m个,不考虑顺序) | |
| * @param m 选取的元素数量 | |
| * @returns 所有可能组合的数组 | |
| */ | |
| combinations(m: number): T[][] { | |
| if (m < 0 || m > this.elements.length) { | |
| throw new Error(`Invalid value for m: ${m}. Must be between 0 and ${this.elements.length}.`); | |
| } | |
| const result: T[][] = []; | |
| // 辅助函数:生成组合 | |
| const generateCombination = (startIndex: number, current: T[] = []): void => { | |
| if (current.length === m) { | |
| result.push([...current]); | |
| return; | |
| } | |
| for (let i = startIndex; i < this.elements.length; i++) { | |
| current.push(this.elements[i]); | |
| generateCombination(i + 1, current); | |
| current.pop(); | |
| } | |
| }; | |
| generateCombination(0); | |
| return result; | |
| } | |
| /** | |
| * 计算全部子组合的数量(长度从 0 到 n 的所有组合) | |
| * @returns 所有子组合的数量 | |
| */ | |
| allSubCombinationsCount(): number { | |
| let totalCount = 0; | |
| for (let m = 0; m <= this.elements.length; m++) { | |
| totalCount += this.combinationCount(m); | |
| } | |
| return totalCount; | |
| } | |
| /** | |
| * 生成所有子组合(长度从 1 到 n 的所有组合) | |
| * @returns 所有子组合的数组 | |
| */ | |
| allSubCombinations(): T[][] { | |
| const result: T[][] = []; | |
| for (let m = 1; m <= this.elements.length; m++) { | |
| result.push(...this.combinations(m)); | |
| } | |
| return result; | |
| } | |
| // #endregion | |
| // #region 全排列与子排列方法 | |
| /** | |
| * 计算全排列数 P(n,n) = n! | |
| * @returns 全排列数 | |
| */ | |
| fullPermutationCount(): number { | |
| return this.permutationCount(this.elements.length); | |
| } | |
| /** | |
| * 生成全排列(从n个元素中取n个的所有排列) | |
| * @returns 全排列数组 | |
| */ | |
| fullPermutation(): T[][] { | |
| return this.permutations(this.elements.length); | |
| } | |
| /** | |
| * 计算所有子排列的数量(长度从 1 到 n 的所有排列) | |
| * @returns 所有子排列的数量 | |
| */ | |
| allSubPermutationsCount(): number { | |
| let totalCount = 0; | |
| for (let m = 1; m <= this.elements.length; m++) { | |
| totalCount += this.permutationCount(m); | |
| } | |
| return totalCount; | |
| } | |
| /** | |
| * 生成所有子排列(长度从 1 到 n 的所有排列) | |
| * @returns 所有子排列的数组 | |
| */ | |
| allSubPermutations(): T[][] { | |
| const result: T[][] = []; | |
| for (let m = 1; m <= this.elements.length; m++) { | |
| result.push(...this.permutations(m)); | |
| } | |
| return result; | |
| } | |
| // #endregion | |
| // #region 笛卡尔积计算 | |
| /** | |
| * 计算多个集合的笛卡尔积 | |
| * @param sets 要计算笛卡尔积的集合数组 | |
| * @returns 笛卡尔积结果 | |
| */ | |
| static cartesianProduct<U>(...sets: U[][]): U[][] { | |
| if (sets.length === 0) return [[]]; | |
| const result: U[][] = []; | |
| const helper = (index: number, current: U[] = []): void => { | |
| if (index === sets.length) { | |
| result.push([...current]); | |
| return; | |
| } | |
| for (const item of sets[index]) { | |
| current.push(item); | |
| helper(index + 1, current); | |
| current.pop(); | |
| } | |
| }; | |
| helper(0); | |
| return result; | |
| } | |
| // #endregion | |
| // #region 私有辅助方法 | |
| /** | |
| * 辅助方法:计算阶乘 | |
| * @param n 要计算阶乘的数 | |
| * @returns 阶乘结果 | |
| */ | |
| private factorial(n: number): number { | |
| if (n < 0) throw new Error("Factorial not defined for negative numbers"); | |
| if (n <= 1) return 1; | |
| let result = 1; | |
| for (let i = 2; i <= n; i++) { | |
| result *= i; | |
| } | |
| return result; | |
| } | |
| // #endregion | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const charSet = new CombinatoricsKit(['A', 'B', 'C', 'D']); | |
| // 排列示例:从4个字符中取2个的排列 | |
| console.log("排列 P(4,2):"); | |
| console.log("理论数量:", charSet.permutationCount(2)); | |
| console.log("排列结果:", charSet.permutations(2)); | |
| // 全排列示例 | |
| console.log("\n全排列:"); | |
| console.log("理论数量:", charSet.fullPermutationCount()); | |
| console.log("全排列结果:", charSet.fullPermutation()); | |
| // 子排列示例 | |
| console.log("\n子排列:"); | |
| console.log("理论数量:", charSet.allSubPermutationsCount()); | |
| console.log("所有子排列结果:", charSet.allSubPermutations()); | |
| // 组合示例:从4个字符中取2个的组合 | |
| console.log("\n组合 C(4,2):"); | |
| console.log("理论数量:", charSet.combinationCount(2)); | |
| console.log("组合结果:", charSet.combinations(2)); | |
| // 子组合示例 | |
| console.log("\n子组合:"); | |
| console.log("理论数量:", charSet.allSubCombinationsCount()); | |
| console.log("所有子组合结果:", charSet.allSubCombinations()); | |
| // 笛卡尔积示例 | |
| console.log("\n笛卡尔积:"); | |
| const product = CombinatoricsKit.cartesianProduct( | |
| ['A', 'B'], | |
| ['1', '2'], | |
| ['X', 'Y'] | |
| ); | |
| console.log("理论数量:", 2 * 2 * 2); // 8 | |
| console.log("笛卡尔积结果:", product); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
排列、组合、笛卡尔积、全排列总结
1. 排列(Permutation)
定义
$$
P(n, m) = \frac{n!}{(n - m)!}
$$
特点
[A, B]和[B, A]是不同的排列。应用场景
注意事项
[1, 1, 2]),需去重处理。2. 组合(Combination)
定义
$$
C(n, m) = \frac{n!}{m!(n - m)!}
$$
特点
[A, B]和[B, A]视为相同组合。应用场景
注意事项
[A, A, B]),需使用组合数公式 $ C(n + m - 1, m) $。3. 笛卡尔积(Cartesian Product)
定义
若集合
$$
A \times B \times C = {(a, b, c) \mid a \in A, b \in B, c \in C}
$$
特点
A=[1,2],B=['a','b'])。(1, 'a')和('a', 1)是不同结果(若集合顺序不同)。应用场景
CROSS JOIN)。注意事项
4. 全排列(Full Permutation)
定义
$$
P(n, n) = n!
$$
特点
应用场景
注意事项
[1, 1, 2]),需去重(如用set或递归剪枝)。5. 对比总结表
6. 实际案例对比
案例1:密码生成
[A,B,C]中生成3位密码(如ABC,ACB, ...),共[A,B,C]中选2个字符(如AB,AC,BC),共[A,B]和[1,2]中生成组合(如(A,1),(A,2), ...),共[A,B,C]中生成所有3位密码,共