多個列表的排列組合(笛卡兒積) | 迭代實現


西電開源社區逛論壇時候,發現下面的排列組合問題有一個高效的迭代方式實現。

如何從 ['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("\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<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--


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM