,關鍵是要找到一種方法來遍歷集合的[0, n[
整數區間permutations
。
置換(在數學上的含義)可以看作是[0, n[
本身的雙射,可以用這個置換的圖像表示,應用於[0, n[
。
例如,考慮的[0, 3[
置換:
0 -> 1
1 -> 2
2 -> 0
它可以被看作是該元組(1, 2, 0)
,其在C,自然轉化爲整數permutation = (int []){1, 2, 0};
的陣列。
假設你有函數指針steps
的數組,然後爲每個排列,你會再想要調用steps[permutation[i]]
,在[0, n[
的i
每個值。
下面的代碼實現該算法:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static void stepA(int data) { printf("%d: %s\n", data, __func__); }
static void stepB(int data) { printf("%d: %s\n", data, __func__); }
static void stepC(int data) { printf("%d: %s\n", data, __func__); }
static void (* const steps[])(int) = {stepA, stepB, stepC,};
static int fact(int n) { return n == 0 ? 1 : fact(n - 1) * n; }
static int compare_int(const void *pa, const void *pb)
{
return *(const int *)pa - *(const int *)pb;
}
static void get_next_permutation(int tab[], size_t n)
{
int tmp;
unsigned i;
unsigned j;
unsigned k;
/* to find the next permutation in the lexicographic order
* source: question 4 (in french, sorry ^^) of
* https://liris.cnrs.fr/~aparreau/Teaching/INF233/TP2-permutation.pdf
. */
/* 1. find the biggest index i for which tab[i] < tab[i+1] */
for (k = 0; k < n - 1; k++)
if (tab[k] < tab[k + 1])
i = k;
/* 2. Find the index j of the smallest element, bigger than tab[i],
* located after i */
j = i + 1;
for (k = i + 1; k < n; k++)
if (tab[k] > tab[i] && tab[k] < tab[j])
j = k;
/* 3. Swap the elements of index i and j */
tmp = tab[i];
tab[i] = tab[j];
tab[j] = tmp;
/* 4. Sort the array in ascending order, after index i */
qsort(tab + i + 1, n - (i + 1), sizeof(*tab), compare_int);
}
int main(void)
{
int n = sizeof(steps)/sizeof(*steps);
int j;
int i;
int permutation[n];
int f = fact(n);
/* first permutation is identity */
for (i = 0; i < n; i++)
permutation[i] = i;
for (j = 0; j < f; j++) {
for (i = 0; i < n; i++)
steps[permutation[i]](i);
if (j != f - 1)
get_next_permutation(permutation, n);
}
return EXIT_SUCCESS;
}
外環在main
,通過j
索引,遍歷所有n!
置換,而內部之一,通過i
索引,迭代接管的n
步驟。
get_next_permutation
修改了permutation
數組,以便按字典順序獲得下一個排列。
注意,它不起作用當輸入置換是最後一個(n - 1, ..., 1, 0)
,因此if (j != f - 1)
測試。 人們可以加強它來檢測這種情況下(我沒有設置),並把第一個排列(0, 1, ..., n - 1)
到permutation
陣列。
的代碼可以編譯:
gcc main.c -o main -Wall -Wextra -Werror -O0 -g3
,我強烈建議使用valgrind
,以此來檢測的off-by-一個錯誤。
編輯:我剛剛意識到我沒有準確回答OP的問題。 someMagic()
函數將允許直接訪問第i個排列,而我的算法只允許按字典順序計算後繼。但是如果目標是迭代所有的排列,它將工作得很好。否則,可能像this one這樣的答案應該符合要求。
這是一個關於如何寫一個置換函數的問題? – Aemyl