Создать последовательность случайных чисел без повторов - PullRequest
37 голосов
/ 29 марта 2009

Дубликат:

Уникальные случайные числа в O (1)?

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

Например:

случайная (10)

может вернуться 5, 9, 1, 4, 2, 8, 3, 7, 6, 10

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


Edit:

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


Edit:

Я вижу, что все предлагают алгоритмы перемешивания. Но если я хочу сгенерировать большое случайное число (1024 байт +), тогда этот метод занял бы намного больше памяти, чем если бы я просто использовал обычный ГСЧ и вставлял его в набор до тех пор, пока он не достигнет указанной длины, верно? Нет лучшего математического алгоритма для этого.

Ответы [ 28 ]

0 голосов
/ 29 марта 2009

Перестановка N элементов не занимает слишком много памяти ... подумайте об этом. Вы меняете только один элемент за раз, поэтому максимальная используемая память - это N + 1 элементов.

0 голосов
/ 09 января 2014

теперь у нас есть массив с разными номерами!

int main() {
    int b[(the number
    if them)];
    for (int i = 0; i < (the number of them); i++) {
    int a = rand() % (the number of them + 1) + 1;
    int j = 0;
    while (j < i) {
        if (a == b[j]) {
        a = rand() % (the number of them + 1) + 1;
        j = -1;
        }
        j++;
    }
    b[i] = a;
    }
}
0 голосов
/ 29 марта 2009

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

0 голосов
/ 01 августа 2012

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

Существует версия алгоритма случайного перемешивания Фишера-Йейтса, называемая версией Дюрстенфельда, которая случайным образом распределяет последовательно полученные элементы в массивы и коллекции при загрузке массива или коллекции.

Одна вещь, которую нужно помнить, это то, что случайное использование Fisher-Yates (AKA Knuth) или версия Durstenfeld, используемая во время загрузки, очень эффективно для массивов объектов, поскольку перемещается только ссылочный указатель на объект, а сам объект не ' Это должно быть изучено или сравнено с любым другим объектом как часть алгоритма.

Я приведу оба алгоритма ниже.

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

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

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

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

Первый алгоритм - это дюрстенфельдская версия алгоритма тасования Фишера-Йейтса (он же Кнут), применяемая к массиву произвольной длины, который просто случайным образом позиционирует целые числа от 0 до длины массива в массиве. Массив не обязательно должен быть массивом целых чисел, но может быть массивом любых объектов, которые получаются последовательно, что фактически делает его массивом ссылочных указателей. Это просто, коротко и очень эффективно

int size = someNumber;
int[] int array = new int[size]; // here is the array to load
int location; // this will get assigned a value before used
// i will also conveniently be the value to load, but any sequentially acquired
// object will work
for (int i = 0; i <= size; i++) { // conveniently, i is also the value to load
      // you can instance or acquire any object at this place in the algorithm to load
      // by reference, into the array and use a pointer to it in place of j
      int j = i; // in this example, j is trivially i
    if (i == 0) { // first integer goes into first location
        array[i] = j; // this may get swapped from here later
    } else { // subsequent integers go into random locations
            // the next random location will be somewhere in the locations
            // already used or a new one at the end
            // here we get the next random location
            // to preserve true randomness without a significant bias
            // it is REALLY IMPORTANT that the newest value could be
            // stored in the newest location, that is, 
            // location has to be able to randomly have the value i
            int location = nextInt(i + 1); // a random value between 0 and i
            // move the random location's value to the new location
            array[i] = array[location];
            array[location] = j; // put the new value into the random location
    } // end if...else
} // end for

Вуаля, теперь у вас уже есть рандомизированный массив.

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

type[] array = new type[size];

// some code that loads array...

// randomly pick an item anywhere in the current array segment, 
// swap it with the top element in the current array segment,
// then shorten the array segment by 1
// just as with the Durstenfeld version above,
// it is REALLY IMPORTANT that an element could get
// swapped with itself to avoid any bias in the randomization
type temp; // this will get assigned a value before used
int location; // this will get assigned a value before used
for (int i = arrayLength -1 ; i > 0; i--) {
    int location = nextInt(i + 1);
    temp = array[i];
    array[i] = array[location];
    array[location] = temp;
} // end for

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

0 голосов
/ 06 декабря 2013

Вот способ случайного выбора без повторения результатов. Это также работает для строк. Это в C #, но логика должна работать во многих местах. Поместите случайные результаты в список и проверьте, есть ли новый случайный элемент в этом списке. Если нет, то у вас есть новый случайный элемент. Если он находится в этом списке, повторяйте случайное число до тех пор, пока не получите элемент, которого нет в этом списке.

List<string> Erledigte = new List<string>();
private void Form1_Load(object sender, EventArgs e)
{
    label1.Text = "";
    listBox1.Items.Add("a");
    listBox1.Items.Add("b");
    listBox1.Items.Add("c");
    listBox1.Items.Add("d");
    listBox1.Items.Add("e");
}

private void button1_Click(object sender, EventArgs e)
{
    Random rand = new Random();
    int index=rand.Next(0, listBox1.Items.Count);
    string rndString = listBox1.Items[index].ToString();

    if (listBox1.Items.Count <= Erledigte.Count)
    {
        return;
    }
    else
    {
        if (Erledigte.Contains(rndString))
        {
            //MessageBox.Show("vorhanden");
            while (Erledigte.Contains(rndString))
            {
                index = rand.Next(0, listBox1.Items.Count);
                rndString = listBox1.Items[index].ToString();
            }
        }

        Erledigte.Add(rndString);
        label1.Text += rndString;
    }
}
0 голосов
/ 29 марта 2009

Предполагая, что у вас есть генератор случайных или псевдослучайных чисел, даже если не гарантируется возвращение уникальных значений, вы можете реализовать тот, который возвращает уникальные значения каждый раз, используя этот код, предполагая, что верхний предел остается постоянным (т.е. Назовите это с random(10), и не называйте это с random(10); random(11).

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

/* the function returns a random number between 0 and max -1
 * not necessarily unique
 * I assume it's written
 */
int random(int max);

/* the function returns a unique random number between 0 and max - 1 */
int unique_random(int max)
{

    static int *list = NULL;    /* contains a list of numbers we haven't returned */
    static int in_progress = 0; /* 0 --> we haven't started randomizing numbers
                                 * 1 --> we have started randomizing numbers
                                 */

    static int count;
    static prev_max = 0;

    // initialize the list
    if (!in_progress || (prev_max != max)) {
        if (list != NULL) {
            free(list);
        }
        list = malloc(sizeof(int) * max);
        prev_max = max;
        in_progress = 1;
        count = max - 1;

        int i;
        for (i = max - 1; i >= 0; --i) {
            list[i] = i;
        }
    }

    /* now choose one from the list */
    int index = random(count);
    int retval = list[index];

    /* now we throw away the returned value.
     * we do this by shortening the list by 1
     * and replacing the element we returned with
     * the highest remaining number
     */
    swap(&list[index], &list[count]);

    /* when the count reaches 0 we start over */
    if (count == 0) {
        in_progress = 0;
        free(list);
        list = 0;
    } else { /* reduce the counter by 1 */
        count--;
    }
}

/* swap two numbers */
void swap(int *x, int *y)
{
    int temp = *x;
    *x = *y;
    *y = temp;
}
0 голосов
/ 25 февраля 2014

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

0 голосов
/ 29 марта 2009

Предположим, вы хотите сгенерировать последовательность из 256 случайных чисел без повторов.

  1. Создать 256-битный (32-байтовый) блок памяти, инициализированный нулями, назовем его b
  2. Ваша циклическая переменная будет n, число чисел, которые еще будут сгенерированы
  3. Петля от n = 256 до n = 1
  4. Генерация случайного числа r в диапазоне [0, n)
  5. Найдите r -й нулевой бит в вашем блоке памяти b, назовем его p
  6. Поместите p в список результатов, массив с именем q
  7. Перевернуть p -й бит в блоке памяти b в 1
  8. После прохода n = 1 вы создали список чисел

Вот краткий пример того, о чем я говорю, используя n = 4 изначально:

**Setup**
b = 0000
q = []

**First loop pass, where n = 4**
r = 2
p = 2
b = 0010
q = [2]

**Second loop pass, where n = 3**
r = 2
p = 3
b = 0011
q = [2, 3]

**Third loop pass, where n = 2**
r = 0
p = 0
b = 1011
q = [2, 3, 0]

** Fourth and final loop pass, where n = 1**
r = 0
p = 1
b = 1111
q = [2, 3, 0, 1]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...