Heap's algorithm to generate permutations
This commit is contained in:
parent
13c6d00ff6
commit
83fcf178a9
|
@ -0,0 +1,48 @@
|
|||
#ifndef ACED_INCLUDES_PERMUTATION_H_
|
||||
#define ACED_INCLUDES_PERMUTATION_H_
|
||||
|
||||
// The permutations are generated according to Heap's method [1].
|
||||
// Because we're not allowed to "yield" (i.e., we don't have co-routines, etc.),
|
||||
// we use a state machine (given by `permutation_generator_t`), adapting the
|
||||
// iterative version of Heap's method to suit this approach.
|
||||
//
|
||||
// References:
|
||||
// [1]: https://en.m.wikipedia.org/wiki/Heap%27s_algorithm
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
size_t len; // The number of elements being permuted.
|
||||
size_t *permutation; // Current permutation, as indices.
|
||||
_Bool exhausted; // Whether there are any permutations left to generate.
|
||||
size_t *stack;
|
||||
size_t pointer;
|
||||
} permutation_generator_t;
|
||||
|
||||
// Create a `permutation_generator_t`.
|
||||
// This needs to be done only once. Afterwards, use only `PermutationReset`.
|
||||
// After getting a new generator, you still need to call `PermutationReset`, or
|
||||
// behaviour is undefined.
|
||||
//
|
||||
// Arguments:
|
||||
// len Number of elements to permute (from `0` to `len-1`).
|
||||
permutation_generator_t PermutationNewGenerator(size_t len);
|
||||
|
||||
// Configure a permutation generator to start generating permutations from the
|
||||
// start.
|
||||
void PermutationReset(permutation_generator_t *permutation_generator);
|
||||
|
||||
// Generate the next permutation.
|
||||
//
|
||||
// Calling this after `permutation_generator->exhausted` is true results in
|
||||
// undefined behaviour.
|
||||
void PermutationNext(permutation_generator_t *permutation_generator);
|
||||
|
||||
// Release the memory associated to a permutation generator.
|
||||
//
|
||||
// This will invalidate the generator.
|
||||
void PermutationFree(permutation_generator_t *permutation_generator);
|
||||
|
||||
#endif //ACED_INCLUDES_PERMUTATION_H_
|
|
@ -0,0 +1,79 @@
|
|||
#include <stdlib.h>
|
||||
#include "../includes/permutation.h"
|
||||
|
||||
permutation_generator_t PermutationNewGenerator(size_t len)
|
||||
{
|
||||
size_t *permutation = malloc(2 * len * sizeof(size_t));
|
||||
size_t *stack = permutation + len;
|
||||
|
||||
permutation_generator_t generator = {
|
||||
.len = len,
|
||||
.permutation = permutation,
|
||||
.stack = stack,
|
||||
};
|
||||
|
||||
return generator;
|
||||
}
|
||||
|
||||
void PermutationReset(permutation_generator_t *permutation_generator)
|
||||
{
|
||||
size_t len = permutation_generator->len;
|
||||
size_t *permutation = permutation_generator->permutation;
|
||||
size_t *stack = permutation_generator->stack;
|
||||
|
||||
// Initialize the permutation array and stack.
|
||||
for (size_t i = 1; i < len; i++)
|
||||
{
|
||||
permutation[i] = i;
|
||||
stack[i] = 0;
|
||||
}
|
||||
|
||||
permutation_generator->pointer = 1;
|
||||
permutation_generator->exhausted = (len == 0);
|
||||
}
|
||||
|
||||
static void _PermutationNext(size_t * restrict permutation, size_t * restrict stack, size_t *stack_ptr, _Bool *exhausted, size_t len) {
|
||||
repeat:
|
||||
if (stack[*stack_ptr] < *stack_ptr) {
|
||||
size_t tmp; // gcc is a good compiler :)
|
||||
if (((*stack_ptr) & 1) == 0) { // Is even
|
||||
// Swap A[0] and A[i]
|
||||
tmp = permutation[0];
|
||||
permutation[0] = permutation[*stack_ptr];
|
||||
permutation[*stack_ptr] = tmp;
|
||||
} else {
|
||||
// Swap A[c[i]] and A[i]
|
||||
tmp = permutation[*stack_ptr];
|
||||
permutation[*stack_ptr] = permutation[stack[*stack_ptr]];
|
||||
permutation[stack[*stack_ptr]] = tmp;
|
||||
}
|
||||
|
||||
// Permutation can now be accessed.
|
||||
|
||||
stack[*stack_ptr] += 1;
|
||||
*stack_ptr = 1;
|
||||
} else {
|
||||
stack[*stack_ptr] = 0;
|
||||
*stack_ptr += 1;
|
||||
if (*stack_ptr < len) {
|
||||
goto repeat;
|
||||
} else {
|
||||
*exhausted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PermutationNext(permutation_generator_t *permutation_generator) {
|
||||
size_t *permutation = permutation_generator->permutation;
|
||||
size_t *stack = permutation_generator->stack;
|
||||
size_t *stack_ptr = &(permutation_generator->pointer);
|
||||
size_t len = permutation_generator->len;
|
||||
_Bool *exhausted = &(permutation_generator->exhausted);
|
||||
_PermutationNext(permutation, stack, stack_ptr, exhausted, len);
|
||||
}
|
||||
|
||||
void PermutationFree(permutation_generator_t *permutation_generator)
|
||||
{
|
||||
free(permutation_generator->permutation);
|
||||
// No need to free permutation_generator->stack, since it's contiguous.
|
||||
}
|
Loading…
Reference in New Issue