我已经在别处发布了以下示例,但让我在这里重复一遍:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
typedef char element_t;
typedef unsigned long permutation_t;
static permutation_t factorial(const permutation_t n)
{
permutation_t i, result = 1;
for (i = 2; i <= n; i++) {
const permutation_t newresult = result * i;
if ((permutation_t)(newresult / i) != result)
return 0;
result = newresult;
}
return result;
}
int permutation(element_t *const buffer,
const element_t *const digits,
const size_t length,
permutation_t index)
{
permutation_t scale;
size_t i, d;
if (!buffer || !digits || length < 1)
return errno = EINVAL;
scale = factorial(length);
if (!scale)
return errno = EMSGSIZE;
if (index >= scale)
return errno = ENOENT;
/* Copy original ordered set to mutable buffer */
memmove(buffer, digits, length * sizeof (element_t));
for (i = 0; i < length - 1; i++) {
scale /= (permutation_t)(length - i);
d = index / scale;
index %= scale;
if (d > 0) {
const element_t c = buffer[i + d];
memmove(buffer + i + 1, buffer + i, d * sizeof (element_t));
buffer[i] = c;
}
}
return 0;
}
factorial() 函数只是对factorial 的缓慢但谨慎的实现。从13开始! > 232,21! > 264,还有 35! > 2128,对于 32、64 或 128 位无符号整数 permutation_t 类型只有极少数可能的结果,最好简单地使用数组查找来代替阶乘(如rcgldr already mentioned)。
permutation(buffer, digits, length, index) 函数接受一个长度为length 的目标buffer,并用digits 的第index'th 排列填充它。 (digits 是只读的,不可变的。)
这不是最快的实现,但它会计算O(length) 时间复杂度的任何排列(忽略memmove() 操作;O(length²) 如果考虑memmove())。这是次优的,因为它使用 memmove() 对目标 buffer 中的项目重新排序,并且每个元素需要两个除法(以及一个具有相同除数的模数)。
考虑到最大实际长度限制(12、20 或 34 个元素,取决于 permutation_t 类型的大小),使用 memmove() 不是问题(因为数据在一个或最多几个缓存行)。
这是线程安全的,只要只有一个线程同时对同一个目标buffer进行操作;多个线程从同一源digits 缓冲区生成不同的目标buffers 是线程安全的。