Der Schlüssel ist, einen Weg finden über den Satz von permutations
des [0, n[
ganzzahligen Intervall zu wiederholen.
Eine Permutation (in der mathematischen Bedeutung) kann als eine Bijektion von [0, n[
in sich selbst gesehen werden und kann durch das Bild dieser Permutation dargestellt werden, angewendet auf [0, n[
.
Betrachten wir zum Beispiel die Permutation von [0, 3[
:
0 -> 1
1 -> 2
2 -> 0
kann als das (1, 2, 0)
Tupel zu sehen, die in C, permutation = (int []){1, 2, 0};
auf das Array von ganzen Zahlen natürlich übersetzen.
Angenommen, Sie ein Array von Funktionszeigern steps
haben, dann für jede Permutation werden, Sie wollen dann steps[permutation[i]]
nennen, für jeden Wert von i
in [0, n[
.
Der folgende Code implementiert diesen Algorithmus:
#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;
}
Die äußere Schleife in main
durch j
indexiert, iteriert über die alle n!
Permutationen, während die innere, durch i
indexiert, iteriert overs die n
Schritte.
Die get_next_permutation
ändert das permutation
Array an Ort und Stelle, um die nächste Permutation in der lexikographischen Reihenfolge zu erhalten.
Beachten Sie, dass es nicht funktioniert, wenn die Permutation in Eingang ist der letzte (n - 1, ..., 1, 0)
, daher der if (j != f - 1)
Test. Man könnte es verbessern, um diesen Fall zu erkennen (i ist nicht gesetzt) und die erste Permutation (0, 1, ..., n - 1)
in das permutation
Array zu setzen.
Der Code kann mit kompiliert werden:
gcc main.c -o main -Wall -Wextra -Werror -O0 -g3
Und ich empfehle valgrind
als eine Möglichkeit, mit Off-by-one Fehler zu erkennen.
EDIT: Ich erkannte gerade, dass ich die Frage des OP nicht genau beantwortete. Die someMagic()
Funktion würde einen direkten Zugriff auf die i-te Permutation ermöglichen, während mein Algorithmus nur die Berechnung des Nachfolgers in der lexikographischen Reihenfolge erlaubt. Aber wenn das Ziel darin besteht, alle Permutationen zu durchlaufen, wird es gut funktionieren. Andernfalls sollte eine Antwort wie this one die Anforderung erfüllen.
ist dies eine Frage, wie eine Permutation Funktion zu schreiben? – Aemyl