关键是要找到一种方法来迭代[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,那么对于每个排列,您将需要为[0, n[ 中i 的每个值调用steps[permutation[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 作为一种检测非一个错误的方法。
编辑:我刚刚意识到我没有准确回答 OP 的问题。 someMagic() 函数将允许直接访问第 i 个排列,而我的算法只允许按字典顺序计算后继。但如果目标是迭代所有排列,它会正常工作。否则,也许像this one 这样的答案应该符合要求。