Итерация в случайном порядке [0..n) без массивов - PullRequest
8 голосов
/ 02 октября 2008

Я знаю пару подпрограмм, которые работают следующим образом:

X n + 1 = Рутина (X n , не более)

Например, что-то вроде генератора LCG:

X n + 1 = (a * X n + c) мод m

В этом генераторе недостаточно параметризации для генерации каждой последовательности.

Функция сна:

X n + 1 = Рутина (X n , макс., Номер перестановки)

Эта подпрограмма, параметризованная индексом в наборе всех перестановок, вернет следующий номер в последовательности. Последовательность может быть произвольно большой (поэтому хранение массива и использование факторических чисел нецелесообразно.

В противном случае, есть ли у кого-нибудь указатели на похожие функции, которые либо не имеют состояния, либо имеют постоянное количество состояний для произвольного 'max', так что они будут повторять перетасованный список.

Ответы [ 6 ]

4 голосов
/ 03 октября 2008

Есть 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.

3 голосов
/ 02 октября 2008

Из моего ответа на другой вопрос :

Это действительно возможно сделать в пространство пропорционально количеству выбранные элементы, а не размер набора, из которого вы выбираете, независимо от того, какая доля общий набор вы выбираете. Ты сделаешь это путем генерации случайного перестановка, затем выбирая из нее как это:

Выберите блочный шифр, например TEA или XTEA. Используйте XOR Fold для уменьшить размер блока до наименьшего сила двух больше, чем набор вы выбираете из. Используйте случайный семя как ключ к шифру. к генерировать элемент n в перестановка, зашифровать шифр. Если номер выхода не в ваш набор, зашифруйте это. Повторять до номер находится внутри набора. На в среднем вам придется сделать меньше, чем два шифрования на сгенерированное число. Это имеет дополнительное преимущество, что если ваше семя криптографически безопасно, как и вся ваша перестановка.

Я написал об этом более подробно здесь .

Конечно, нет никакой гарантии, что каждая перестановка может быть сгенерирована (и в зависимости от размера вашего блока и размера ключа, что может даже не быть возможным), но перестановки, которые вы можете получить, очень случайны (если они не были, это не будет хорошим шифром), и вы можете иметь их столько, сколько захотите.

1 голос
/ 02 октября 2008

Если вам нужна функция, которая занимает меньше места в стеке, вам следует использовать итерационную версию, а не функцию. Вы также можете использовать структуру данных, такую ​​как TreeMap, хранить ее на диске и читать по мере необходимости.

X(n+1) = Routine(Xn, max, permutation number)
for(i = n; i > 0; i--)
 {
   int temp = Map.lookup(i) 
   otherfun(temp,max,perm)
 }
0 голосов
/ 03 октября 2008

Код, который использует итеративный интерфейс. Временная сложность 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, переносимо такой же, как короткий, и такой же маленький, как 16 бит). 9! = 362880> 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);
    }
}
0 голосов
/ 03 октября 2008

Код, распаковывающий индекс перестановки в массив с определенным отображением от индекса к перестановке. Есть множество других, но этот удобен.

#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);
}
0 голосов
/ 02 октября 2008

Можно ли индексировать набор перестановок без предварительного вычисления и сохранения всего этого в памяти? Я пробовал что-то подобное раньше и не нашел решения - я думаю, что это невозможно (в математическом смысле).

Отказ от ответственности: возможно, я неправильно понял ваш вопрос ...

...