python多个列表排列组合_多个列表的排列组合(笛卡儿积) | 迭代实现

在西电开源社区逛论坛时候,发现下面的排列组合问题有一个高效的迭代方式实现。

如何从 ['ABC', '12'] 得到 A1 A2 B1 B2 C1 C2

然后推广到 ['abcd', '98h40ui', 'f', 'AY', ...] 这种一般情况

就是一个不定长的列表中包含多个项,每个项中只拿出来一个元素,然后列出所有可能的组合

容易得到,所有可能的组合方案总数为 \(len_1 \cdot len_2 \cdot ... \cdot len_k\)(\(len_i\)为第i个字符串的长度)

如何不用dfs方式去枚举每个列表的选择呢?

进制转换问题

我们回想K进制的计数原理:K进制数的每一位数字为 0~K-1,如10进制 4321,数值大小表示从 0001 到 4321 之间编码的个数。

要分离 4321 每一位上的数字,则按如下操作不停取模获得余数(从低位到高位):

BASE = 10;

while(N) {

bit = N % BASE;

printf("%d", bit);

N /= 10;

}

进制转化的问题也是如此,如将BASE改为2,则上述算法得到十进制数N的二进制表示。

变进制思想

对于该问题,每个列表的长度是不同的,可以设想我们使用一个变化进制的计数方式,将方案总数转化成该进制的数。依次从小到大遍历所有编码,分离出编码的每一位,即表示每个列表实际选取的下标。

变进制在全排列中也有运用,可以计算得到一个排列的字典序。全排列用到的阶乘数系的 BASE为 k!。

例:有排列 35241 ,我们从数字2开始看,2右侧有1个比它小的数字,数字3右侧有2个,数字4右侧有1个,数字5右侧有3个,我们将这些逆序数倒着写下来是:3,1,2,1,则该序列在我们这种排序方法中的位置序号是:

3x4!+1x3!+2x2!+1x1! = 83 注意,排序是从0开始计数的。

算法实现

#include

#include

#include

using namespace std;

// 迭代方式直接打印结果

void printCombination(const char *words[], int num) {

int totCnt = 1;

for (int i=0;i

for (int code=0;code

int codeNow = code;

for (int i=0;i

int base = strlen(words[i]);

int bit = codeNow % base;

codeNow /= base;

printf("%c", words[i][bit]);

}

printf("\n");

}

}

// 递归方式

void dfs(const char *words[], int num, int k, char now[]) {

if (k>=num) {

now[k] = '\0';

printf("%s\n", now);

return;

}

for(int i=0;i

now[k] = words[k][i];

dfs(words, num, k+1, now);

}

}

int main() {

const char *words[3] = {"ABC", "1234", "XY"};

printCombination(words, 3);

// char now[4];

// dfs(strings, 3, 0, now);

return 0;

}

Python实现

作为一门简洁、优雅的语言,对于这种繁杂的问题当然有更好的写法

Python标准库itertools为我们提供了非常方便的排列组合操作,itertools 模块提供的迭代器函数主要有三种类型

无限迭代器:生成一个无限序列

有限迭代器:接收一个或多个序列作为参数,进行组合、分组和过滤等

组合生成器:序列的排列、组合,求序列的笛卡儿积等

product:笛卡尔积

permutations:排列

combinations:组合

combinations-with-replacement:生成的组合包含自身元素

代码实现:

from itertools import product

words = ["HOW", "ARE", "YOU"]

for item in product(*words):

print("".join(item))

--End--

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值