2015-03-04 5 views
5

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

Я знаю you can't have full randomness without storing full array, но мне не нужен порядок, чтобы быть действительно случайным. Мне нужно, чтобы это воспринималось пользователем как случайное. Решение должно использовать сублинейное пространство.

Одно возможное предложение - с использованием большого простого номера - given here. Проблема с этим решением заключается в том, что существует очевидный фиксированный шаг (размер блока принятого модуля). Я бы предпочел решение, которое не так явно неслучайно. Есть ли лучшее решение?

+1

Вы довольны посещением данного элемента более одного раза или нет? т. е. вы ищете случайную перестановку элементов или для случайной последовательности элементов? Вопрос, который вы цитируете, касается первой из этих проблем (также называемой перетасовкой). – Walter

+1

Множество (большинство?) Генераторов псевдослучайных чисел могут быть определены алгоритмом и целым числом или двумя, чтобы описать их текущее состояние. Учитывая эти факторы, их будущее поведение полностью детерминировано, хотя, по-видимому, случайное - вот что означает * псевдо *. Что вы ищете, это не PRNG? –

+0

Очевидно, мне нужно избегать повторений (в противном случае я могу просто перейти к случайному элементу). Я редактировал вопрос, добавляя эту информацию. Фактически, желаемое решение должно сохранять текущее состояние таким образом, чтобы можно было перемещать N элементов из N шагов из любого состояния. – Michal

ответ

2

Как насчет этого алгоритма?

К псевдо-псевдослучайному перемещению массива размера n.

  1. Создать небольшой массив размера к
  2. использовать большой метод простого числа, чтобы заполнить небольшой массив, г = 0
  3. Случайный удалить позицию с помощью RNG из небольшого массива, I + = 1
  4. , если я < п - к а затем добавить новую позицию с использованием больших простых чисел метод
  5. , если я < п Гото 3.

й e выше k - тем больше случайности, которую вы получаете. Такой подход позволит вам отложить генерацию чисел из метода простых чисел.

Аналогичный подход может быть выполнен для генерации числа раньше, чем ожидалось в последовательности, путем создания другого массива «skip-list». Случайно выбирайте элементы позже в последовательности, используйте их, чтобы пересечь следующую позицию, а затем добавьте их в список пропусков. Когда они, естественно, приходят, их ищут в skip-списке и подавляют, а затем удаляют из списка пропуска, после чего вы можете случайно добавить еще один элемент в список пропуска.

+0

В итоге у меня была вариация темы скиписта. Все элементы генерируются методом Large Prime Number, но каждый раз, когда мне нужно выбрать номер, я сначала добавляю случайное число элементов в skiplist (до небольшого фиксированного размера). Затем я либо выбираю случайным образом из скиписта, либо возвращаю следующее число, вычисленное методом больших простых чисел (это также определяется случайным образом). Его очень легко реализовать, и цифры выглядят действительно случайными. – Michal

0

Как указывалось другими, вы можете создать своего рода «план полета», перетасовывая массив индексов массива, а затем следуйте за ним. Это нарушает «будет лучше, если текущее состояние может быть сохранено в нескольких целых числах», но действительно ли это имеет значение? Существуют ли ограниченные ограничения производительности? В конце концов, я считаю, что если вы не принимаете повторения, вам нужно хранить предметы, которые вы уже где-то посещали или каким-то образом.

В качестве альтернативы, вы можете выбрать для навязчивых раствора и хранить bool внутри каждый элемент массива, говорите ли уже был выбран элемент или нет. Это можно сделать почти чистым способом, используя наследование (несколько по необходимости).
Многие проблемы приходят с этим решением, например. и, конечно же, он нарушает ограничение «держать массив без изменений».

+0

И снова OP не требовал хранения массива в таком же размере, или, другими словами, он просит решение в o (n) пространстве [малое здесь]. – amit

+0

Я понял это, и на самом деле я это очень четко указал. Я предлагаю частичные альтернативные решения, которые ослабляют некоторые ограничения и могут быть, возможно, хорошим компромиссом. Например, интрузивное решение может сэкономить несколько байтов на элемент по сравнению с перетасовкой массива индексов. Вопрос: прочитал ли вы ответ? ;) – gd1

+0

Возможно, это может быть объединено с использованием генератора случайных чисел, семя которого может быть принудительно. Программа выбрала бы одно случайное семя. Каждый раз, когда требуются перетасованные данные, он может быть воспроизведен из исходного массива и семени. –

0

Квадратичные вычеты, которые вы упомянули («с использованием большого штриха») хорошо известны, будут работать и гарантировать повторение каждого элемента ровно один раз (если это требуется, но, похоже, это не так?). К несчастью, они не «очень случайны», и есть несколько других требований к модулю в дополнение к тому, чтобы быть простым для его работы.
На странице Джеффа Прешинга есть страница, которая подробно описывает технику и предлагает feed the output of the residue generator into the generator again with a fixed offset.

Однако, поскольку вы сказали, что вам просто нужно «воспринимать как случайное от пользователя», кажется, что вы можете сделать с подачей хэш-функции (скажем, cityhash или siphash) с целыми целыми числами. Выход будет «случайным» целым числом, а , по крайней мере, до, будет строгое отображение 1: 1 (так как существует намного больше возможных значений хэша, чем есть входы).

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

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

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

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

1

Идея случайного генератора, который имитирует перетасовку, хороша, если вы можете получить тот, чей максимальный период вы можете контролировать.

Linear Congruential Generator вычисляет случайное число с формулой:

x[i + 1] = (a * x[i] + c) % m; 

Максимальный период м и это достигается тогда, когда выполнены следующие свойства:

  • Параметры с и m относительно простые.
  • Для каждого простого числа г разделяющей м, - 1 кратно г.
  • Если м кратно 4, то также - 1 кратно 4.

Мой первый darft Приглашена делает м следующий кратное 4 после длины массива, а затем найти подходит a и c значения. Это была (а) большая работа и (б) давала очень очевидные результаты.

Я переосмыслил этот подход. Мы можем сделать м наименьшей мощностью двух, в которой будет располагаться длина массива. Единственный простой коэффициент m - это 2, что сделает каждое нечетное число относительно простым. За исключением 1 и 2, м будет делится на 4, что означает, что мы должны сделать - 1 кратно 4.

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

В следующем коде приведены псевдослучайные числа с периодом превышения m. Я избегал тривиальных значений для a и c и на моих (не слишком многочисленных) пятнах, результаты выглядели хорошо. По крайней мере, не было очевидного велосипедного рисунка.

Итак:

class RandomIndexer 
{ 
public: 
    RandomIndexer(size_t length) : len(length) 
    { 
     m = 8; 
     while (m < length) m <<= 1; 

     c = m/6 + uniform(5 * m/6); 
     c |= 1; 

     a = m/12 * uniform(m/6); 
     a = 4*a + 1; 
     x = uniform(m);      
    } 

    size_t next() 
    { 
     do { x = (a*x + c) % m; } while (x >= len); 

     return x; 
    } 

private: 
    static size_t uniform(size_t m) 
    { 
     double p = std::rand()/(1.0 + RAND_MAX); 

     return static_cast<int>(m * p); 
    } 

    size_t len; 
    size_t x; 
    size_t a; 
    size_t c; 
    size_t m; 
}; 

Вы можете использовать генератор, как это:

std::vector<int> list; 
for (size_t i = 0; i < 3; i++) list.push_back(i); 

RandomIndexer ix(list.size()); 
for (size_t i = 0; i < list.size(); i++) { 
    std::cout << list[ix.next()]<< std::endl; 
} 

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

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

+0

Вы уверены, '' 'while (p * p Michal

+0

Другая проблема с этим решением заключается в том, что он не «выглядит» случайным - легко видеть, что существует фиксированный шаг между двумя последующими элементами. – Michal

+0

Вы правы. Мы должны найти все факторы. В тех случаях, когда _m_ является произведением числа 4 и простого числа, _a_ эффективно 1. (Это _m_ + 1, но по модулю арифметика означает, что оно равно 1.) Я скорректировал код. –

0

Это java-решение, которое можно легко преобразовать в C++ и аналогично решению M Oehm выше, хотя и с другим способом выбора параметров LCG.

import java.util.Enumeration; 
    import java.util.Random; 

    public class RandomPermuteIterator implements Enumeration<Long> { 
     int c = 1013904223, a = 1664525; 
     long seed, N, m, next; 
     boolean hasNext = true; 

     public RandomPermuteIterator(long N) throws Exception { 
      if (N <= 0 || N > Math.pow(2, 62)) throw new Exception("Unsupported size: " + N); 
      this.N = N; 
      m = (long) Math.pow(2, Math.ceil(Math.log(N)/Math.log(2))); 
      next = seed = new Random().nextInt((int) Math.min(N, Integer.MAX_VALUE)); 
     } 

     public static void main(String[] args) throws Exception { 
      RandomPermuteIterator r = new RandomPermuteIterator(100); 
      while (r.hasMoreElements()) System.out.print(r.nextElement() + " "); 
      //output:50 52 3 6 45 40 26 49 92 11 80 2 4 19 86 61 65 44 27 62 5 32 82 9 84 35 38 77 72 7 ... 
     } 

     @Override 
     public boolean hasMoreElements() { 
      return hasNext; 
     } 

     @Override 
     public Long nextElement() { 
      next = (a * next + c) % m; 
      while (next >= N) next = (a * next + c) % m; 
      if (next == seed) hasNext = false; 
      return next; 
     } 
    } 
Смежные вопросы