it-swarm.com.ru

Создать последовательность случайных чисел без повторов

Дубликат:

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

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

Например:

случайное (10)

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

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


Правка:

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


Правка:

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

37
Unknown

Возможно, вас заинтересует сдвиговый регистр с линейной обратной связью ... Мы использовали его для сборки из аппаратного обеспечения, но я также делал это программно. Он использует сдвиговый регистр с некоторыми из битов, xor'ed и возвращаемых на вход, и если вы выберете только правильные «касания», вы можете получить последовательность, которая будет равной размеру регистра. То есть 16-битный lfsr может создать последовательность длиной 65535 без повторов. Это статистически случайный, но, конечно, чрезвычайно повторяемый. Кроме того, если это сделано неправильно, вы можете получить несколько удивительно короткие последовательности. Если вы посмотрите на lfsr, вы найдете примеры того, как их правильно построить (то есть, «максимальная длина»).

28
gbarry

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

17
Mitch Wheat

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

Подробнее о перемешивании здесь: Создание случайного упорядоченного списка из упорядоченного списка

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

В этом случае поиск по списку пока будет намного более эффективным. Таким образом, идеальным вариантом будет использование эвристики (определенной экспериментом), чтобы выбрать правильную реализацию для заданных аргументов. (Извиняюсь за то, что приводил примеры на C #, а не на C++, но ASFAC++ B Я тренирую себя думать на C #).

IEnumerable<int> GenerateRandomNumbers(int range, int quantity)
{
    int[] a = new int[quantity];

    if (range < Threshold)
    {
        for (int n = 0; n < range; n++)
            a[n] = n;

        Shuffle(a);
    }
    else
    {
        HashSet<int> used = new HashSet<int>();

        for (int n = 0; n < quantity; n++)
        {
            int r = Random(range);

             while (!used.Add(r))
                 r = Random(range);

             a[n] = r;
        }
    }

    return a;
}

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

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

Также для больших количеств и больших диапазонов может быть предпочтительным вернуть объект, который производит числа в последовательности по запросу, вместо выделения массива для результатов заранее. Это очень легко реализовать в C # благодаря ключевому слову yield return:

IEnumerable<int> ForLargeQuantityAndRange(int quantity, int range)
{
    for (int n = 0; n < quantity; n++)
    {
        int r = Random(range);

        while (!used.Add(r))
            r = Random(range);

        yield return r;
    }
}
16
Daniel Earwicker

Если гарантируется, что случайное число никогда не повторится, оно больше не является случайным, и количество случайности уменьшается при генерировании чисел (после девяти чисел функция random(10) довольно предсказуема, и даже после только восьми у вас есть шанс 50-50) ,.

7
Motti

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

Вместо этого используйте обратимый псевдослучайный хеш. Затем введите значения 0 1 2 3 4 5 6 и т.д. По очереди.

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

Вот тот, который будет работать, например, если вы хотите просмотреть все 2 ^ 32 32-битных значений. Проще всего написать, потому что неявный мод 2 ^ 32 целочисленной математики работает в ваших интересах в этом случае.

unsigned int reversableHash(unsigned int x)
{
   x*=0xDEADBEEF;
   x=x^(x>>17);
   x*=0x01234567;
   x+=0x88776655;
   x=x^(x>>4);
   x=x^(x>>9);
   x*=0x91827363;
   x=x^(x>>7);
   x=x^(x>>11);
   x=x^(x>>20);
   x*=0x77773333;
   return x;
}
4
SPWorley

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

3
starblue

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


Ответ на изменения и комментарии:

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

3
Bill the Lizard

Как насчет использования генератора GUID (как в .NET). Конечно, не гарантируется, что дубликатов не будет, однако вероятность их получения довольно мала.

2
Rashack

Когда вы генерируете свои номера, используйте фильтр Блума , чтобы обнаружить дубликаты. Это будет использовать минимальный объем памяти. Там не было бы необходимости хранить более ранние номера в серии вообще.

Компромисс в том, что ваш список не может быть исчерпывающим в вашем диапазоне. Если ваши числа действительно порядка 256 ^ 1024, это вряд ли компромисс.

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

1
Imbue

Я второй ответ gbarry об использовании LFSR . Они очень эффективны и просты в реализации даже в программном обеспечении и гарантированно не повторяются в (2 ^ N - 1) использования для LFSR с N-битным регистром сдвига.

Однако есть некоторые недостатки: наблюдая небольшое количество выходов из ГСЧ, можно восстановить ЛФСР и предсказать все значения, которые он сгенерирует, что делает их непригодными для криптографии и где бы ни требовался хороший ГСЧ. Вторая проблема заключается в том, что либо слово «все ноль», либо слово «все одно» (в битах) недопустимо в зависимости от реализации LFSR. Третья проблема, которая имеет отношение к вашему вопросу, состоит в том, что максимальное число, генерируемое LFSR, всегда является степенью 2 - 1 (или степенью 2 - 2).

Первый недостаток может не быть проблемой в зависимости от вашего приложения. Из приведенного вами примера кажется, что вы не ожидаете, что среди ответов будет ноль; Итак, вторая проблема не имеет отношения к вашему случаю . Проблема максимального значения (и, следовательно, диапазона) может быть решена путем повторного использования LFSR, пока вы не получите число в пределах вашего диапазона. Вот пример:

Скажем, вы хотите иметь числа от 1 до 10 (как в вашем примере). Вы бы использовали 4-битный LFSR с диапазоном [1, 15] включительно. Вот псевдокод о том, как получить число в диапазоне [1,10]:

x = LFSR.getRandomNumber();
while (x > 10) {
   x = LFSR.getRandomNumber();
}

Вы должны встроить предыдущий код в свой RNG; так что вызывающий не будет заботиться о реализации . Обратите внимание, что это замедлит работу вашего RNG, если вы используете большой сдвиговый регистр, а максимальное требуемое число не является степенью 2 - 1.

1
gsarkis

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

1
Nick Johnson

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

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

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

1
Brian Campbell

Если вы не имеете в виду плохие статистические свойства сгенерированной последовательности, есть один метод:

Допустим, вы хотите сгенерировать N чисел, каждое из 1024 бит. Вы можете пожертвовать некоторые биты сгенерированного числа, чтобы быть «счетчиком».

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

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

Таким образом, вы уверены, что каждый раз получаете уникальный номер.

Я имею в виду, например, что каждое сгенерированное число выглядит следующим образом: Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyxxxxyxyyyyxxyxx , Где x - непосредственно из генератора, а y - из переменной счетчика.

0
ajuc

Пожалуйста, проверьте ответы на

Генерировать последовательность целых чисел в случайном порядке, не создавая весь список заранее

а также мой ответ лежит там как

 very simple random is 1+((power(r,x)-1) mod p) will be from 1 to p for values of x from 1 to p and will be random where r and p are prime numbers and r <> p.
0
lakshmanaraj

Предполагая, что у вас есть генератор случайных или псевдослучайных чисел, даже если он не гарантирует возвращение уникальных значений, вы можете реализовать тот, который возвращает уникальные значения каждый раз, используя этот код, предполагая, что верхний предел остается постоянным (т.е. вы всегда вызываете его с 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
Nathan Fellman

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

0
Jason Punyon
static std::unordered_set<long> s;
long l = 0;
for(; !l && (s.end() != s.find(l)); l = generator());
v.insert(l);

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

Я сделал это с длинным для примера, но вы должны сделать это шаблоном, если ваш PRNG шаблонизирован.

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

0
Edouard A.

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

0
John Smith

Вот способ случайного выбора без повторения результатов. Это также работает для строк. Это в 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
Johnny

Предположим, вы хотите сгенерировать серию из 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]
0
William Brendel

Я задавал подобный вопрос раньше, но мой был для всего диапазона int см. Поиск хэш-функции/Упорядоченный Int/to/Shuffled Int/

0
David Allan Finch

Проблема состоит в том, чтобы выбрать «случайную» последовательность из N уникальных чисел из диапазона 1..M, где нет ограничений на отношения между N и M (M может быть намного больше, примерно таким же или даже меньше, чем N; они не могут быть относительно простыми). 

Расширяя ответ регистров сдвига с линейной обратной связью: для данного M постройте максимальный LFSR для наименьшей степени двух, которая больше M. Затем просто возьмите свои числа из LFSR, выбрасывающих числа больше M. В среднем вы будете выбрасывать не более половины сгенерированных чисел (поскольку по построению более половины диапазона LFSR меньше, чем M), поэтому ожидаемое время получения числа равно O (1). Вы не храните ранее сгенерированные числа, поэтому потребление пространства также составляет O(1). Если вы выполняете цикл перед получением N чисел, то M меньше N (или LFSR построен неправильно).

Вы можете найти параметры для максимальной длины LFSR до 168 бит здесь (из Википедии): http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf

Вот немного кода Java:

/ ** * Генерация последовательности уникальных «случайных» чисел в [0, M) * @author dkoes * * /

открытый класс UniqueRandom { long lfsr; длинная маска; длинный максимум;

private static long seed = 1;
//indexed by number of bits
private static int [][] taps = {
        null, // 0
        null, // 1
        null, // 2
        {3,2}, //3
        {4,3},
        {5,3},
        {6,5},
        {7,6},
        {8,6,5,4},
        {9,5},
        {10,7},
        {11,9},
        {12,6,4,1},
        {13,4,3,1},
        {14,5,3,1},
        {15,14},
        {16,15,13,4},
        {17,14},
        {18,11},
        {19,6,2,1},
        {20,17},
        {21,19},
        {22,21},
        {23,18},
        {24,23,22,17},
        {25,22},
        {26,6,2,1},
        {27,5,2,1},
        {28,25},
        {29,27},
        {30,6,4,1},
        {31,28},
        {32,22,2,1},
        {33,20},
        {34,27,2,1},
        {35,33},
        {36,25},
        {37,5,4,3,2,1},
        {38,6,5,1},
        {39,35},
        {40,38,21,19},
        {41,38},
        {42,41,20,19},
        {43,42,38,37},
        {44,43,18,17},
        {45,44,42,41},
        {46,45,26,25},
        {47,42},
        {48,47,21,20},
        {49,40},
        {50,49,24,23},
        {51,50,36,35},
        {52,49},
        {53,52,38,37},
        {54,53,18,17},
        {55,31},
        {56,55,35,34},
        {57,50},
        {58,39},
        {59,58,38,37},
        {60,59},
        {61,60,46,45},
        {62,61,6,5},
        {63,62},
};

//m is upperbound; things break if it isn't positive
UniqueRandom(long m)
{
    max = m;
    lfsr = seed; //could easily pass a starting point instead
    //figure out number of bits
    int bits = 0;
    long b = m;
    while((b >>>= 1) != 0)
    {
        bits++;
    }
    bits++;

    if(bits < 3)
        bits = 3; 

    mask = 0;
    for(int i = 0; i < taps[bits].length; i++)
    {
        mask |= (1L << (taps[bits][i]-1));
    }

}

//return -1 if we've cycled
long next()
{
    long ret = -1;
    if(lfsr == 0)
        return -1;
    do {
        ret = lfsr;
        //update lfsr - from wikipedia
        long lsb = lfsr & 1;
        lfsr >>>= 1;
        if(lsb == 1)
            lfsr ^= mask;

        if(lfsr == seed)            
            lfsr = 0; //cycled, stick

        ret--; //zero is stuck state, never generated so sub 1 to get it
    } while(ret >= max);

    return ret;
}

}

0
dkoes

Мерсенн твистер

Описание которых можно найти здесь, в Википедии: Mersenne twister

Посмотрите в нижней части страницы для реализации на разных языках.

0
Indy9000

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

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

Следует помнить одну вещь: случайное перемешивание Фишера-Йейтса (AKA Knuth) или версия Durstenfeld, используемые во время загрузки, очень эффективны для массивов объектов, поскольку перемещается только ссылочный указатель на объект, а сам объект не должен быть изучены или сравнены с любым другим объектом как часть алгоритма. 

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

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

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

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

Оба алгоритма предполагают, что у вас есть некоторый метод экземпляра случайного числа, nextInt (int setSize), который генерирует случайное целое число от нуля до setSize, что означает наличие возможных значений setSize. В этом случае это будет размер массива, поскольку последний индекс массива равен size-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
Jim