Heap's algorithm to generate permutations

This commit is contained in:
Miguel M 2023-04-29 09:58:13 +01:00
parent 13c6d00ff6
commit 83fcf178a9
2 changed files with 127 additions and 0 deletions

48
includes/permutation.h Normal file
View File

@ -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_

79
src/permutation.c Normal file
View File

@ -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.
}