我知道几个例程如下:
X n + 1 =例程(X n ,max)
例如,类似于LCG生成器:
X n + 1 =(a * X n + c)mod m
此生成器中没有足够的参数化来生成每个序列。
梦想功能:
X n + 1 =例程(X n ,max,置换数)
此例程通过索引到所有排列集合中进行参数化,将返回序列中的下一个数字。序列可能是任意大的(因此存储数组并使用事实数字是不切实际的。
如果失败了,是否有人指向类似的函数,这些函数要么是无状态的,要么具有任意'max'的常量状态,这样它们就会迭代一个混洗列表。
答案 0 :(得分:4)
有n! n个元素的排列。存储您正在使用的那个至少需要log(n!)/ log(2)位。通过斯特林的近似,这大约需要n log(n)/ log(2)位。
显式存储一个索引需要log(n)/ log(2)位。存储所有n,如在索引数组中需要n倍,或者再次n log(n)/ log(2)。信息 - 理论上,没有比明确存储排列更好的方法。
换句话说,您传入所需集合中的排列的索引采用相同的渐近存储空间,就像写出排列一样。例如,如果您将置换的索引限制为32位值,则只能处理最多12个元素的排列。 64位索引最多只能获得20个元素。
由于索引占用与置换相同的空间,因此要么将表示更改为直接使用置换,要么接受解压缩为大小为N的数组。
答案 1 :(得分:3)
从我对another question的回复:
实际上可以这样做 空间与数量成正比 选择的元素,而不是 您选择的集合的大小, 不管比例是多少 你要选择的总数。你做 这通过生成随机 置换,然后从中进行选择 像这样:
选择一个分组密码,例如TEA 或XTEA。使用XOR folding来 将块大小减小到最小 两个大于集合的力量 你在选择。随机使用 种子是密码的关键。至 生成一个元素n 置换,加密n 密码。如果输出的数字不在 你的设置,加密。重复直到 数字在集合内。上 平均而言,你将不得不做到 每个生成的数字有两个加密。 这有额外的好处,如果 你的种子是加密安全的, 你的整个排列也是如此。
我更详细地写了这篇文章 here
当然,无法保证每个排列都可以生成(并且取决于您的块大小和密钥大小,甚至可能无法实现),但您可以获得的排列是高度随机的(如果不是,它不是一个好的密码),你可以随心所欲地拥有它们。
答案 2 :(得分:1)
如果您想要一个占用较少堆栈空间的函数,那么您应该考虑使用迭代版本而不是函数。您还可以使用类似TreeMap的数据结构,并将其存储在磁盘上,并根据需要进行读取。
X(n+1) = Routine(Xn, max, permutation number)
for(i = n; i > 0; i--)
{
int temp = Map.lookup(i)
otherfun(temp,max,perm)
}
答案 3 :(得分:0)
是否有可能在没有事先计算并将整个事物存储在内存中的情况下索引一组排列?我之前尝试过这样的事情并没有找到解决方案 - 我认为这是不可能的(在数学意义上)。
免责声明:我可能误解了你的问题...
答案 4 :(得分:0)
将排列索引解包到数组中的代码,具有从索引到排列的特定映射。还有很多其他的,但这个很方便。
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
typedef unsigned char index_t;
typedef unsigned int permutation;
static void permutation_to_array(index_t *indices, index_t n, permutation p)
{
index_t used = 0;
for (index_t i = 0; i < n; ++i) {
index_t left = n - i;
index_t digit = p % left;
for (index_t j = 0; j <= digit; ++j) {
if (used & (1 << j)) {
digit++;
}
}
used |= (1 << digit);
indices[i] = digit;
p /= left;
}
}
static void dump_array(index_t *indices, index_t n)
{
fputs("[", stdout);
for (index_t i = 0; i < n; ++i) {
printf("%d", indices[i]);
if (i != n - 1) {
fputs(", ", stdout);
}
}
puts("]");
}
static int factorial(int n)
{
int prod = 1;
for (int i = 1; i <= n; ++i) {
prod *= i;
}
return prod;
}
int main(int argc, char **argv)
{
const index_t n = 4;
const permutation max = factorial(n);
index_t *indices = malloc(n * sizeof (*indices));
for (permutation p = 0; p < max; ++p) {
permutation_to_array(indices, n, p);
dump_array(indices, n);
}
free(indices);
}
答案 5 :(得分:0)
使用迭代接口的代码。时间复杂度为O(n ^ 2),空间复杂度具有开销:n(log n位)的复制,迭代变量(log n位),跟踪ni(log n位),当前值的副本(log n位),p(n log n位)的副本,下一个值的创建(log n位)和一组使用值(n位)。您无法避免n log n位的开销。时间,这也是O(n ^ 2),用于设置位。这可以减少一点,但代价是使用装饰树来存储使用的值。
这可以通过使用对相应库的调用来改变使用任意精度整数和位集,并且上面的边界实际上将开始启动,而不是以N = 8为上限,可移植(int可以是相同的短,小至16位)。 9! = 362880&gt; 65536 = 2 ^ 16
#include <math.h>
#include <stdio.h>
typedef signed char index_t;
typedef unsigned int permutation;
static index_t permutation_next(index_t n, permutation p, index_t value)
{
permutation used = 0;
for (index_t i = 0; i < n; ++i) {
index_t left = n - i;
index_t digit = p % left;
p /= left;
for (index_t j = 0; j <= digit; ++j) {
if (used & (1 << j)) {
digit++;
}
}
used |= (1 << digit);
if (value == -1) {
return digit;
}
if (value == digit) {
value = -1;
}
}
/* value not found */
return -1;
}
static void dump_permutation(index_t n, permutation p)
{
index_t value = -1;
fputs("[", stdout);
value = permutation_next(n, p, value);
while (value != -1) {
printf("%d", value);
value = permutation_next(n, p, value);
if (value != -1) {
fputs(", ", stdout);
}
}
puts("]");
}
static int factorial(int n)
{
int prod = 1;
for (int i = 1; i <= n; ++i) {
prod *= i;
}
return prod;
}
int main(int argc, char **argv)
{
const index_t n = 4;
const permutation max = factorial(n);
for (permutation p = 0; p < max; ++p) {
dump_permutation(n, p);
}
}