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

88

В этом случае MAX составляет всего 5, поэтому я мог бы проверять дубликаты один за другим, но как я могу сделать это проще? Например, что, если MAX имеет значение 20? Спасибо.

int MAX = 5;

for (i = 1 , i <= MAX; i++)
{
        drawNum[1] = (int)(Math.random()*MAX)+1;

        while (drawNum[2] == drawNum[1])
        {
             drawNum[2] = (int)(Math.random()*MAX)+1;
        }
        while ((drawNum[3] == drawNum[1]) || (drawNum[3] == drawNum[2]) )
        {
             drawNum[3] = (int)(Math.random()*MAX)+1;
        }
        while ((drawNum[4] == drawNum[1]) || (drawNum[4] == drawNum[2]) || (drawNum[4] == drawNum[3]) )
        {
             drawNum[4] = (int)(Math.random()*MAX)+1;
        }
        while ((drawNum[5] == drawNum[1]) ||
               (drawNum[5] == drawNum[2]) ||
               (drawNum[5] == drawNum[3]) ||
               (drawNum[5] == drawNum[4]) )
        {
             drawNum[5] = (int)(Math.random()*MAX)+1;
        }

}
Вун-Суп Юнг
источник
3
Многие (псевдо) генераторы случайных чисел не повторяются в течение своего полного «цикла». Проблема, конечно, в том, что их полный «цикл» состоит из миллиардов или триллионов значений, и значения, которые они производят, могут быть любыми из этих миллиардов или триллионов значений. Теоретически вы могли бы создать генератор случайных чисел с «циклом» 5 или 10 или что-то еще, но это, вероятно, больше проблем, чем оно того стоит.
Hot Licks
1
Также генератор случайных чисел, который не повторяется, даже «менее» случайен: если MAX = 5 и вы читаете 3 числа, вы можете угадать следующее с вероятностью 50%, если вы прочитаете 4 числа, вы знаете следующее на 100%. конечно!
icza
Ответил на повторяющийся вопрос здесь
Alex - GlassEditor.com

Ответы:

149

Самый простой способ - создать список возможных чисел (1..20 или что-то еще), а затем перемешать их Collections.shuffle. Затем просто возьмите столько элементов, сколько захотите. Это замечательно, если ваш диапазон равен количеству элементов, которые вам нужны в конце (например, для перетасовки колоды карт).

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

if (max < numbersNeeded)
{
    throw new IllegalArgumentException("Can't ask for more numbers than are available");
}
Random rng = new Random(); // Ideally just create one instance globally
// Note: use LinkedHashSet to maintain insertion order
Set<Integer> generated = new LinkedHashSet<Integer>();
while (generated.size() < numbersNeeded)
{
    Integer next = rng.nextInt(max) + 1;
    // As we're adding to a set, this will automatically do a containment check
    generated.add(next);
}

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

Еще один вариант - всегда добиваться прогресса, каждый раз уменьшая диапазон и компенсируя существующие значения. Так, например, предположим, что вам нужно 3 значения в диапазоне 0..9. На первой итерации вы генерируете любое число в диапазоне 0..9 - скажем, вы генерируете 4.

На второй итерации вы должны сгенерировать число в диапазоне 0..8. Если сгенерированное число меньше 4, вы оставите его как есть ... в противном случае вы добавите к нему единицу. Это даст вам диапазон результатов 0..9 без 4. Предположим, мы получили 7 таким образом.

На третьей итерации вы должны сгенерировать число в диапазоне 0..7. Если сгенерированное число меньше 4, вы оставите его как есть. Если это 4 или 5, вы должны добавить один. Если это 6 или 7, вы должны добавить два. Таким образом, диапазон результатов будет 0..9 без 4 или 6.

Джон Скит
источник
Сгенерируйте массив возможных значений, случайным образом выберите одно (размер массива случайных чисел), удалите (и сохраните) выбранное число, затем повторите.
Hot Licks
Или используйте генератор случайных чисел с полным циклом (генераторы, основанные на простых числах, могут использовать маленькие простые числа - с соответствующими небольшими циклами) и отбрасывать значения за пределы допустимого диапазона.
Поль де Вриз,
«Еще один вариант - всегда добиваться прогресса» - это WAAAAY лучшее решение. Пожалуйста, отредактируйте, чтобы отразить. И спасибо за прекрасный ответ.
user123321
1
@musselwhizzle: В ближайшее время постараюсь найти время. Я не уверен насчет «WAAAY лучше» - он будет значительно менее «очевидно правильным», хотя и более эффективным. Довольно часто я с удовольствием жертвую производительностью ради удобочитаемости.
Джон Скит,
@Deepthi: Какой максимум хочет ОП - в соответствии с вопросом.
Джон Скит,
19

Вот как бы я это сделал

import java.util.ArrayList;
import java.util.Random;

public class Test {
    public static void main(String[] args) {
        int size = 20;

        ArrayList<Integer> list = new ArrayList<Integer>(size);
        for(int i = 1; i <= size; i++) {
            list.add(i);
        }

        Random rand = new Random();
        while(list.size() > 0) {
            int index = rand.nextInt(list.size());
            System.out.println("Selected: "+list.remove(index));
        }
    }
}

Как заметил уважаемый мистер Скит:
если n - это количество случайно выбранных чисел, которые вы хотите выбрать, а N - это общее пространство выборки чисел, доступных для выбора:

  1. Если n << N , вы должны просто сохранить выбранные числа и проверить список, чтобы увидеть, есть ли в нем выбранное число.
  2. Если n ~ = N , вам, вероятно, следует использовать мой метод, заполняя список, содержащий все пространство выборки, а затем удаляя из него числа по мере их выбора.
Catchwa
источник
list должен быть LinkedList, удаление случайных индексов из arrayylist очень неэффективно
Риккардо Касатта,
@RiccardoCasatta есть ли у вас источник для вашего утверждения? Я также не могу представить, что обход связанного списка был бы очень эффективным. См. Также: stackoverflow.com/a/6103075/79450
Catchwa
Я протестировал и вы правы, стоит ли мне удалить свой комментарий?
Риккардо Касатта
@RiccardoCasatta Другим может быть полезен наш обмен
мнениями
13
//random numbers are 0,1,2,3 
ArrayList<Integer> numbers = new ArrayList<Integer>();   
Random randomGenerator = new Random();
while (numbers.size() < 4) {

    int random = randomGenerator .nextInt(4);
    if (!numbers.contains(random)) {
        numbers.add(random);
    }
}
Сатиш Гудури
источник
Это было бы ужасно для больших чисел. ArrayList.contains выполняет итерацию по списку. Гораздо чище было бы вместо этого иметь Set - вам не нужно проверять, содержит ли он, просто добавьте, и производительность будет лучше.
kfox,
5

Есть еще один способ создания "случайных" упорядоченных чисел с помощью LFSR, посмотрите:

http://en.wikipedia.org/wiki/Linear_feedback_shift_register

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

Но это не ИСТИННЫЕ случайные числа, потому что случайное поколение детерминировано.

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

Вот алгоритм LFSR на java, (я где-то его не помню):

public final class LFSR {
    private static final int M = 15;

    // hard-coded for 15-bits
    private static final int[] TAPS = {14, 15};

    private final boolean[] bits = new boolean[M + 1];

    public LFSR() {
        this((int)System.currentTimeMillis());
    }

    public LFSR(int seed) {
        for(int i = 0; i < M; i++) {
            bits[i] = (((1 << i) & seed) >>> i) == 1;
        }
    }

    /* generate a random int uniformly on the interval [-2^31 + 1, 2^31 - 1] */
    public short nextShort() {
        //printBits();

        // calculate the integer value from the registers
        short next = 0;
        for(int i = 0; i < M; i++) {
            next |= (bits[i] ? 1 : 0) << i;
        }

        // allow for zero without allowing for -2^31
        if (next < 0) next++;

        // calculate the last register from all the preceding
        bits[M] = false;
        for(int i = 0; i < TAPS.length; i++) {
            bits[M] ^= bits[M - TAPS[i]];
        }

        // shift all the registers
        for(int i = 0; i < M; i++) {
            bits[i] = bits[i + 1];
        }

        return next;
    }

    /** returns random double uniformly over [0, 1) */
    public double nextDouble() {
        return ((nextShort() / (Integer.MAX_VALUE + 1.0)) + 1.0) / 2.0;
    }

    /** returns random boolean */
    public boolean nextBoolean() {
        return nextShort() >= 0;
    }

    public void printBits() {
        System.out.print(bits[M] ? 1 : 0);
        System.out.print(" -> ");
        for(int i = M - 1; i >= 0; i--) {
            System.out.print(bits[i] ? 1 : 0);
        }
        System.out.println();
    }


    public static void main(String[] args) {
        LFSR rng = new LFSR();
        Vector<Short> vec = new Vector<Short>();
        for(int i = 0; i <= 32766; i++) {
            short next = rng.nextShort();
            // just testing/asserting to make 
            // sure the number doesn't repeat on a given list
            if (vec.contains(next))
                throw new RuntimeException("Index repeat: " + i);
            vec.add(next);
            System.out.println(next);
        }
    }
}
фелипе
источник
4

Другой подход, который позволяет вам указать, сколько чисел вы хотите sizeиспользовать, minа также maxзначения и возвращаемых чисел.

public static int getRandomInt(int min, int max) {
    Random random = new Random();

    return random.nextInt((max - min) + 1) + min;
}

public static ArrayList<Integer> getRandomNonRepeatingIntegers(int size, int min,
        int max) {
    ArrayList<Integer> numbers = new ArrayList<Integer>();

    while (numbers.size() < size) {
        int random = getRandomInt(min, max);

        if (!numbers.contains(random)) {
            numbers.add(random);
        }
    }

    return numbers;
}

Чтобы использовать его, он возвращает 7 чисел от 0 до 25.

    ArrayList<Integer> list = getRandomNonRepeatingIntegers(7, 0, 25);
    for (int i = 0; i < list.size(); i++) {
        System.out.println("" + list.get(i));
    }
Карло Родригес
источник
4

Это было бы намного проще java-8:

Stream.generate(new Random()::ints)
            .distinct()
            .limit(16) // whatever limit you might need
            .toArray(Integer[]::new);
Евгений
источник
3

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

// get 5 unique random numbers, possible values 0 - 19
// (assume desired number of selections < number of choices)

const int POOL_SIZE = 20;
const int VAL_COUNT = 5;

declare Array mapping[POOL_SIZE];
declare Array results[VAL_COUNT];

declare i int;
declare r int;
declare max_rand int;

// create mapping array
for (i=0; i<POOL_SIZE; i++) {
   mapping[i] = i;
}

max_rand = POOL_SIZE-1;  // start loop searching for maximum value (19)

for (i=0; i<VAL_COUNT; i++) {
    r = Random(0, max_rand); // get random number
    results[i] = mapping[r]; // grab number from map array
    mapping[r] = max_rand;  // place item past range at selected location

    max_rand = max_rand - 1;  // reduce random scope by 1
}

Предположим, что первая итерация сгенерировала для начала случайное число 3 (от 0 до 19). Это приведет к тому, что results [0] = mapping [3], то есть значение 3. Затем мы присвоим mapping [3] 19.

На следующей итерации случайное число было 5 (от 0 до 18). Это сделало бы results [1] = mapping [5], то есть значение 5. Затем мы присвоили бы map [5] значение 18.

Теперь предположим, что следующая итерация снова выбрала 3 (от 0 до 17). results [2] будет присвоено значение mapping [3], но теперь это значение не 3, а 19.

Такая же защита сохраняется для всех номеров, даже если вы получили один и тот же номер 5 раз подряд. Например, если генератор случайных чисел выдаст вам 0 пять раз подряд, результат будет: [0, 19, 18, 17, 16].

Вы никогда не получите одно и то же число дважды.

blackcatweb
источник
Я сомневаюсь, что это так же случайно, как вы думаете. Проходит ли он стандартные тесты на случайность ?; казалось бы, концентрирует числа ближе к концу спектра.
tucuxi
Вот базовый вариант. Пул - {a, b, c}. Нам нужно 2 неповторяющихся элемента. Следуя алгоритму, вот комбинации, которые мы могли бы нарисовать, и их результаты: 0,0: a, c 0,1: a, b 1,0: b, a 1,1: b, c 2,0: c, a 2, 1: c, b Счет: a-4, b-4, c-4
blackcatweb
3

Создание всех индексов последовательности, как правило, является плохой идеей, так как это может занять много времени, особенно если соотношение выбираемых чисел MAXнизкое (сложность становится преобладающей O(MAX)). Ситуация ухудшается, если отношение чисел, которые нужно выбрать, MAXприближается к единице, поскольку тогда удаление выбранных индексов из последовательности всех также становится дорогостоящим (мы приближаемся O(MAX^2/2)). Но для небольших чисел это обычно работает хорошо и не особенно подвержено ошибкам.

Фильтрация сгенерированных индексов с помощью коллекции также является плохой идеей, поскольку некоторое время тратится на вставку индексов в последовательность, и прогресс не гарантируется, поскольку одно и то же случайное число может быть нарисовано несколько раз (но для достаточно больших MAXэто маловероятно ). Это может быть близко к сложности
O(k n log^2(n)/2), игнорируя дубликаты и предполагая, что коллекция использует дерево для эффективного поиска (но со значительной постоянной стоимостью kвыделения узлов дерева и, возможно, с необходимостью перебалансировки ).

Другой вариант - однозначно сгенерировать случайные значения с самого начала, чтобы гарантировать прогресс. Это означает, что в первом раунде создается случайный индекс [0, MAX]:

items i0 i1 i2 i3 i4 i5 i6 (total 7 items)
idx 0       ^^             (index 2)

Во втором раунде [0, MAX - 1]генерируется только (поскольку один элемент уже был выбран):

items i0 i1    i3 i4 i5 i6 (total 6 items)
idx 1          ^^          (index 2 out of these 6, but 3 out of the original 7)

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

Для коротких последовательностей это довольно быстрый O(n^2/2)алгоритм:

void RandomUniqueSequence(std::vector<int> &rand_num,
    const size_t n_select_num, const size_t n_item_num)
{
    assert(n_select_num <= n_item_num);

    rand_num.clear(); // !!

    // b1: 3187.000 msec (the fastest)
    // b2: 3734.000 msec
    for(size_t i = 0; i < n_select_num; ++ i) {
        int n = n_Rand(n_item_num - i - 1);
        // get a random number

        size_t n_where = i;
        for(size_t j = 0; j < i; ++ j) {
            if(n + j < rand_num[j]) {
                n_where = j;
                break;
            }
        }
        // see where it should be inserted

        rand_num.insert(rand_num.begin() + n_where, 1, n + n_where);
        // insert it in the list, maintain a sorted sequence
    }
    // tier 1 - use comparison with offset instead of increment
}

Где n_select_numтвоя 5 и n_number_numтвоя MAX. В n_Rand(x)возвращает случайные числа в [0, x](включительно). Это можно сделать немного быстрее, если выбрать много элементов (например, не 5, а 500), используя двоичный поиск для поиска точки вставки. Для этого нам нужно убедиться, что мы отвечаем требованиям.

Мы будем выполнять бинарный поиск со сравнением, n + j < rand_num[j]аналогичным
n < rand_num[j] - j. Нам нужно показать, что rand_num[j] - jэто все еще отсортированная последовательность для отсортированной последовательности rand_num[j]. К счастью, это легко показать, поскольку наименьшее расстояние между двумя элементами оригинала rand_numравно единице (сгенерированные числа уникальны, поэтому всегда существует разница не менее 1). В то же время, если мы вычтем индексы jиз всех элементов
rand_num[j], разница в индексах будет ровно 1. Таким образом, в «худшем» случае мы получим постоянную последовательность, но никогда не убывающую. Таким образом, можно использовать двоичный поиск, который дает O(n log(n))алгоритм:

struct TNeedle { // in the comparison operator we need to make clear which argument is the needle and which is already in the list; we do that using the type system.
    int n;

    TNeedle(int _n)
        :n(_n)
    {}
};

class CCompareWithOffset { // custom comparison "n < rand_num[j] - j"
protected:
    std::vector<int>::iterator m_p_begin_it;

public:
    CCompareWithOffset(std::vector<int>::iterator p_begin_it)
        :m_p_begin_it(p_begin_it)
    {}

    bool operator ()(const int &r_value, TNeedle n) const
    {
        size_t n_index = &r_value - &*m_p_begin_it;
        // calculate index in the array

        return r_value < n.n + n_index; // or r_value - n_index < n.n
    }

    bool operator ()(TNeedle n, const int &r_value) const
    {
        size_t n_index = &r_value - &*m_p_begin_it;
        // calculate index in the array

        return n.n + n_index < r_value; // or n.n < r_value - n_index
    }
};

И наконец:

void RandomUniqueSequence(std::vector<int> &rand_num,
    const size_t n_select_num, const size_t n_item_num)
{
    assert(n_select_num <= n_item_num);

    rand_num.clear(); // !!

    // b1: 3578.000 msec
    // b2: 1703.000 msec (the fastest)
    for(size_t i = 0; i < n_select_num; ++ i) {
        int n = n_Rand(n_item_num - i - 1);
        // get a random number

        std::vector<int>::iterator p_where_it = std::upper_bound(rand_num.begin(), rand_num.end(),
            TNeedle(n), CCompareWithOffset(rand_num.begin()));
        // see where it should be inserted

        rand_num.insert(p_where_it, 1, n + p_where_it - rand_num.begin());
        // insert it in the list, maintain a sorted sequence
    }
    // tier 4 - use binary search
}

Я проверил это на трех тестах. Сначала было выбрано 3 числа из 7 элементов, а гистограмма выбранных элементов была собрана за 10 000 прогонов:

4265 4229 4351 4267 4267 4364 4257

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

Второй тест включал выбор 7 чисел из 5000 пунктов. Время нескольких версий алгоритма было набрано более 10 000 000 прогонов. Результаты обозначаются в комментариях кода как b1. Простая версия алгоритма немного быстрее.

Третий тест включал выбор 700 номеров из 5000 элементов. Снова набралось время нескольких версий алгоритма, на этот раз более 10 000 прогонов. Результаты обозначаются в комментариях кода как b2. Версия алгоритма бинарного поиска теперь более чем в два раза быстрее, чем простая.

Второй метод начинает быстрее выбирать более 75 элементов на моей машине (обратите внимание, что сложность любого алгоритма не зависит от количества элементов MAX).

Стоит отметить, что приведенные выше алгоритмы генерируют случайные числа в порядке возрастания. Но было бы просто добавить еще один массив, в котором числа будут сохраняться в том порядке, в котором они были сгенерированы, и вместо этого вернуть его (с незначительной дополнительной стоимостью O(n)). Нет необходимости перетасовывать вывод: это будет намного медленнее.

Обратите внимание, что исходники написаны на C ++, у меня на машине нет Java, но концепция должна быть ясной.

ИЗМЕНИТЬ :

Для развлечения я также реализовал подход, который генерирует список со всеми индексами
0 .. MAX, выбирает их случайным образом и удаляет их из списка, чтобы гарантировать уникальность. Поскольку я выбрал достаточно высокое MAX(5000), производительность катастрофическая:

// b1: 519515.000 msec
// b2: 20312.000 msec
std::vector<int> all_numbers(n_item_num);
std::iota(all_numbers.begin(), all_numbers.end(), 0);
// generate all the numbers

for(size_t i = 0; i < n_number_num; ++ i) {
    assert(all_numbers.size() == n_item_num - i);
    int n = n_Rand(n_item_num - i - 1);
    // get a random number

    rand_num.push_back(all_numbers[n]); // put it in the output list
    all_numbers.erase(all_numbers.begin() + n); // erase it from the input
}
// generate random numbers

Я также реализовал подход с помощью set(коллекции C ++), которая фактически занимает второе место в тесте b2, будучи лишь примерно на 50% медленнее, чем подход с двоичным поиском. Это понятно, так как setиспользуется двоичное дерево, стоимость вставки которого аналогична двоичному поиску. Единственное отличие - шанс получить повторяющиеся предметы, что замедляет прогресс.

// b1: 20250.000 msec
// b2: 2296.000 msec
std::set<int> numbers;
while(numbers.size() < n_number_num)
    numbers.insert(n_Rand(n_item_num - 1)); // might have duplicates here
// generate unique random numbers

rand_num.resize(numbers.size());
std::copy(numbers.begin(), numbers.end(), rand_num.begin());
// copy the numbers from a set to a vector

Полный исходный код здесь .

свинья
источник
2

Вы можете использовать один из классов, реализующих интерфейс Set ( API ), а затем каждое сгенерированное вами число использовать Set.add () для его вставки.

Если возвращаемое значение ложно, вы знаете, что число уже было сгенерировано ранее.

С.С. Винрова
источник
2

Вместо того, чтобы делать все это, создайте LinkedHashSetобъект и случайные числа для него по Math.random()функции .... если произойдет какая-либо повторяющаяся запись, LinkedHashSetобъект не будет добавлять это число в свой список ... Поскольку в этом классе коллекции не допускаются повторяющиеся значения .. в итоге вы получите список случайных чисел, не имеющих повторяющихся значений ....: D

Абдеали Чандан
источник
2

Кажется, ваша проблема сводится к выбору k элементов случайным образом из набора n элементов. Таким образом, ответ Collections.shuffle правильный, но, как указано, неэффективный: его O (n).

Википедия: перетасовка Фишера – Йейтса имеет версию O (k), когда массив уже существует. В вашем случае нет массива элементов, и создание массива элементов может быть очень дорогостоящим, скажем, если бы max было 10000000 вместо 20.

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

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

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

  1. Выберите k случайных чисел: первое находится в диапазоне от 0 до n-1, второе от 0 до n-2, третье от 0 до n-3 и так далее до nk.

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

int getValue(i) { if (map.contains(i)) return map[i]; return i; } void setValue(i, val) { if (i == val) map.remove(i); else map[i] = val; } int[] chooseK(int n, int k) { for (int i = 0; i < k; i++) { int randomIndex = nextRandom(0, n - i); //(n - i is exclusive) int desiredIndex = n-i-1; int valAtRandom = getValue(randomIndex); int valAtDesired = getValue(desiredIndex); setValue(desiredIndex, valAtRandom); setValue(randomIndex, valAtDesired); } int[] output = new int[k]; for (int i = 0; i < k; i++) { output[i] = (getValue(n-i-1)); } return output; }

дхаким
источник
creating the array of elements could be very expensive- почему создание массива должно быть дороже, чем перетасовка? Я думаю, что нет абсолютно никаких оснований для пессимизма в этом вопросе :-)
Wolf
1

Следующий код создает случайное число между [1, m], которое не было сгенерировано ранее.

public class NewClass {

    public List<Integer> keys = new ArrayList<Integer>();

    public int rand(int m) {
        int n = (int) (Math.random() * m + 1);
        if (!keys.contains(n)) {
            keys.add(n);
            return n;
        } else {
            return rand(m);
        }
    }

    public static void main(String[] args) {
        int m = 4;
        NewClass ne = new NewClass();
        for (int i = 0; i < 4; i++) {
            System.out.println(ne.rand(m));
        }
        System.out.println("list: " + ne.keys);
    }
}
ParisaN
источник
0

Есть алгоритм партии карточек: вы создаете упорядоченный массив чисел («пакет карточек») и на каждой итерации выбираете из него число в случайной позиции (конечно, удаляя выбранное число из «партии карточек»).

Лавир Белый
источник
0

Вот эффективное решение для быстрого создания рандомизированного массива. После рандомизации вы можете просто выбрать n-й элемент eмассива, увеличить nи вернуть e. Это решение имеет O (1) для получения случайного числа и O (n) для инициализации, но в качестве компромисса требуется хороший объем памяти, если n становится достаточно большим.

Мартин Турау
источник
0

Существует более эффективное и менее громоздкое решение для целых чисел, чем Collections.shuffle.

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

Этот алгоритм работает для загрузки любого массива и достижения случайного порядка в конце загрузки. Он также работает для добавления в коллекцию List (или любую другую проиндексированную коллекцию) и достижения случайной последовательности в коллекции в конце добавления.

Это может быть сделано с помощью единственного массива, созданного один раз, или численно упорядоченной коллекции, такой как список, на месте. Для массива начальный размер массива должен быть точным размером, чтобы содержать все предполагаемые значения. Если вы заранее не знаете, сколько значений может появиться, можно также использовать коллекцию с числовым порядком, например ArrayList или List, размер которых не является неизменным. Он будет работать универсально для массива любого размера вплоть до Integer.MAX_VALUE, что составляет чуть более 2000000000. Объекты списка будут иметь такие же ограничения индекса. На вашем компьютере может закончиться память, прежде чем вы дойдете до массива такого размера. Может быть более эффективным загрузить массив, типизированный для типов объектов, и преобразовать его в некоторую коллекцию после загрузки массива. Это особенно верно, если целевая коллекция не индексируется численно.

Этот алгоритм в точности так, как написано, создаст очень равномерное распределение без дубликатов. ОЧЕНЬ ВАЖНЫЙ аспект заключается в том, что вставка следующего элемента должна быть возможна до текущего размера +1. Таким образом, для второго элемента можно было бы сохранить его в ячейке 0 или ячейке 1. • 20-й элемент можно было бы сохранить в любом месте, от 0 до 19. Это так же возможно, что первый элемент останется в ячейке 0, как и в любом другом месте. Следующий новый элемент может оказаться куда угодно, включая следующее новое местоположение.

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

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

// RandomSequence.java
import java.util.Random;
public class RandomSequence {

    public static void main(String[] args) {
        // create an array of the size and type for which
        // you want a random sequence
        int[] randomSequence = new int[20];
        Random randomNumbers = new Random();

        for (int i = 0; i < randomSequence.length; i++ ) {
            if (i == 0) { // seed first entry in array with item 0
                randomSequence[i] = 0; 
            } else { // for all other items...
                // choose a random pointer to the segment of the
                // array already containing items
                int pointer = randomNumbers.nextInt(i + 1);
                randomSequence[i] = randomSequence[pointer]; 
                randomSequence[pointer] = i;
                // note that if pointer & i are equal
                // the new value will just go into location i and possibly stay there
                // this is VERY IMPORTANT to ensure the sequence is really random
                // and not biased
            } // end if...else
        } // end for
        for (int number: randomSequence) {
                System.out.printf("%2d ", number);
        } // end for
    } // end main
} // end class RandomSequence
Джим
источник
0

На самом деле все зависит от того, для чего вам нужна случайная генерация, но вот мой вывод.

Сначала создайте автономный метод для генерации случайного числа. Обязательно учитывайте ограничения.

public static int newRandom(int limit){
    return generatedRandom.nextInt(limit);  }

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

public static int testDuplicates(int int1, int int2, int int3, int int4, int int5){
    boolean loopFlag = true;
    while(loopFlag == true){
        if(int1 == int2 || int1 == int3 || int1 == int4 || int1 == int5 || int1 == 0){
            int1 = newRandom(75);
            loopFlag = true;    }
        else{
            loopFlag = false;   }}
    return int1;    }

Вышеупомянутое сравнивает int1 с int2 через int5, а также проверяет, нет ли нулей в случайных числах.

С помощью этих двух методов мы можем сделать следующее:

    num1 = newRandom(limit1);
    num2 = newRandom(limit1);
    num3 = newRandom(limit1);
    num4 = newRandom(limit1);
    num5 = newRandom(limit1);

С последующим:

        num1 = testDuplicates(num1, num2, num3, num4, num5);
        num2 = testDuplicates(num2, num1, num3, num4, num5);
        num3 = testDuplicates(num3, num1, num2, num4, num5);
        num4 = testDuplicates(num4, num1, num2, num3, num5);
        num5 = testDuplicates(num5, num1, num2, num3, num5);

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

Надеюсь это поможет. Этот сайт так мне помог, что я почувствовал себя обязанным хотя бы ПОПРОБОВАТЬ помочь.

AzerDraco
источник
0

Самый простой способ - использовать nano DateTime в качестве длинного формата. System.nanoTime ();

Linh àm Quang
источник