zoukankan      html  css  js  c++  java
  • 多个列表的排列组合(笛卡儿积) | 迭代实现

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

    如何从 ['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,数值大小表示从 00014321 之间编码的个数。
    要分离 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 <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    // 迭代方式直接打印结果
    void printCombination(const char *words[], int num) {
        int totCnt = 1;
        for (int i=0;i<num;i++) totCnt *= strlen(words[i]);
        
        for (int code=0;code<totCnt;code++) {
            int codeNow = code;
            for (int i=0;i<num;i++) {
                int base = strlen(words[i]);
                int bit = codeNow % base;
                codeNow /= base;
                printf("%c", words[i][bit]);
            }
            printf("
    ");
        }
    }
    
    // 递归方式
    void dfs(const char *words[], int num, int k, char now[]) {
        if (k>=num) {
              now[k] = '';
              printf("%s
    ", now);
              return;
        }
    	
        for(int i=0;i<strlen(words[k]);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--

  • 相关阅读:
    ROM、RAM、DRAM、SRAM和FLASH的区别
    寄存器读写为什么需要用位操作符
    不同变量存放在什么地方
    C语言中数据类型对变量的作用
    内存寻址、对齐,变量左值和右值
    位、字节、半字、字、内存位宽
    面试题10- II. 青蛙跳台阶问题
    509. 斐波那契数
    面试题10- I. 斐波那契数列
    面试题32
  • 原文地址:https://www.cnblogs.com/izcat/p/13616126.html
Copyright © 2011-2022 走看看