源自 Knuth 《The Art of Computer Programming Vol.4》,此演算法以混合基數思想,通過單一陣列模擬多位計數器,以 O(1) 摊還時間生成下一組合,無需遞迴、無需多層迴圈,極其優雅高效。
a[1..n] 表示 n 位數,初始全 0a[n] 每次 +1a[j] == radix(達到基數),歸零並向高位進位 j--a[1] 進位溢出(j==0),結束關鍵技巧:分配 n+1 個元素,a[0] 作為哨兵,進位到 0 即為終止條件,省去額外判斷。
void mixed_radix_count(int n, int radix, void (*callback)(int*, int)) {
if (n <= 0 || radix <= 1) return;
int* a = (int*)calloc(n + 1, sizeof(int)); // a[0] 哨兵,a[1..n] 數位
if (!a) return;
while (1) {
callback(a + 1, n); // 傳遞有效數位起始位址
int j = n;
while (a[j] == radix - 1) { // 當前位達最大值
a[j] = 0; // 歸零
--j; // 向高位進位
}
if (j == 0) break; // 哨兵位被進位 → 全排列結束
++a[j]; // 高位 +1
}
free(a);
}
void print_digits(int* digits, int n) {
for (int i = 0; i < n; ++i) printf("%d", digits[i]);
printf("\n");
}
int main() {
mixed_radix_count(3, 3, print_digits);
return 0;
}
// 輸出:
// 000
// 001
// 002
// 010
// 011
// ...
// 222
Knuth 原演算法支援每位基數不同(如日期:日 30、月 12、年無上限)。只需將 radix 改為陣列 radices[1..n]:
void mixed_radix_general(int n, const int radices[], void (*cb)(int*, int)) {
int* a = calloc(n + 1, sizeof(int));
while (1) {
cb(a + 1, n);
int j = n;
while (a[j] == radices[j] - 1) { a[j] = 0; --j; }
if (j == 0) break;
++a[j];
}
free(a);
}
| 指標 | 複雜度 | 說明 |
|---|---|---|
| 時間 | O(radixⁿ) | 必須產生所有組合,輸出敏感 |
| 摊還單次 | O(1) | 平均每次僅修改 1~2 位 |
| 空間 | O(n) | 單一陣列 + 遞迴深度 O(1) |
此演算法以極簡代碼實現通用計數器,是組合生成領域的「Hello World」級基石。掌握其哨兵位與進位邏輯,舉一反三可解決多維迭代、灰碼生成、排列組合等進階問題。