Генерация уникальных случайных чисел в Java


88

Я пытаюсь получить случайные числа от 0 до 100. Но я хочу, чтобы они были уникальными, а не повторялись в последовательности. Например, если у меня 5 чисел, они должны быть 82,12,53,64,32, а не 82,12,53,12,32. Я использовал это, но он генерирует одинаковые числа в последовательности.

Random rand = new Random();
selected = rand.nextInt(100);

5
Вы можете создать случайную перестановку диапазона 1..100(для этого есть известные алгоритмы), но остановитесь после определения первых nэлементов.
Kerrek SB



Ответы:


145
  • Последовательно добавьте каждое число из диапазона в структуру списка .
  • Перемешайте .
  • Возьмите первую букву «н».

Вот простая реализация. Это напечатает 3 уникальных случайных числа из диапазона 1-10.

import java.util.ArrayList;
import java.util.Collections;

public class UniqueRandomNumbers {

    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        for (int i=1; i<11; i++) {
            list.add(new Integer(i));
        }
        Collections.shuffle(list);
        for (int i=0; i<3; i++) {
            System.out.println(list.get(i));
        }
    }
}

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

Вот почему числа совпадают. RandomЭкземпляр высевают на текущий момент времени в миллисекундах. Для определенного начального значения «случайный» экземпляр вернет точно такую ​​же последовательность псевдослучайных чисел.

ЗАМЕТКА, что public Integer​(int value)конструктор deprecatedначиная с Java 9.

Первый цикл for можно просто изменить на:

for (int i = 1; i < 11; i++) {
  list.add(i);
}

3
+1 за указание на случайный случай и ответ на вопрос. :)
Марк Байерс

Необязательно перемешивать весь диапазон. Если вам нужно n уникальных чисел, вам нужно перемешать только первую n позицию, используя перемешивание Фишера-Йейтса. Это может помочь с большим списком и маленьким n.
rossum

59

В Java 8+ вы можете использовать intsметод, Randomчтобы затем получить IntStreamслучайные значения distinctи limitуменьшить поток до числа уникальных случайных значений.

ThreadLocalRandom.current().ints(0, 100).distinct().limit(5).forEach(System.out::println);

Randomтакже есть методы, которые создают LongStreams иDoubleStream s, если они вам нужны.

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

List<Integer> range = IntStream.range(0, 100).boxed()
        .collect(Collectors.toCollection(ArrayList::new));
Collections.shuffle(range);
range.subList(0, 99).forEach(System.out::println);

Мне это нужно для некоторого кода, который я тестирую, и Arrays#setAll()он немного быстрее, чем поток. Итак: `Integer [] index = new Integer [n]; Arrays.setAll (индексы, i -> i); Collections.shuffle (Arrays.asList (индексы)); return Arrays.stream (индексы) .mapToInt (Integer :: intValue) .toArray (); `
AbuNassar

18
  1. Создайте массив из 100 чисел, а затем рандомизируйте их порядок.
  2. Придумайте генератор псевдослучайных чисел с диапазоном 100.
  3. Создайте логический массив из 100 элементов, затем установите для элемента значение true, когда выберете это число. Когда вы выбираете следующий номер, проверьте массив и попробуйте еще раз, установлен ли элемент массива. (Вы можете создать простой для очистки логический массив с массивом, в longкотором вы сдвигаете и маскируете для доступа к отдельным битам.)

2
+1 за альтернативный подход; pick()это пример.
trashgod

1
Вместо использования логического массива вы можете использовать HashSet, где вы храните числа, которые вы уже сгенерировали, и используете их containsдля проверки, если вы уже сгенерировали это число. HashSet, Вероятно , будет немного медленнее , чем булева массива, но занимают меньше памяти.
Рори О'Кейн

1
@ RoryO'Kane - Я почти уверен, что логический массив займет меньше места, если будет реализован как массив long [2]. Ни в коем случае нельзя сделать HashSet таким маленьким.
Hot Licks

Последний подход немного уродлив, поскольку в нем не будет четко определенного количества шагов для генерации всей последовательности. Также не нужно изобретать велосипед - BitSet .
Павел Хорал,


13

Мне кажется, об этом методе стоит упомянуть.

   private static final Random RANDOM = new Random();    
   /**
     * Pick n numbers between 0 (inclusive) and k (inclusive)
     * While there are very deterministic ways to do this,
     * for large k and small n, this could be easier than creating
     * an large array and sorting, i.e. k = 10,000
     */
    public Set<Integer> pickRandom(int n, int k) {
        final Set<Integer> picked = new HashSet<>();
        while (picked.size() < n) {
            picked.add(RANDOM.nextInt(k + 1));
        }
        return picked;
    }

9

Я повторно факторизовал ответ Ананда, чтобы использовать не только уникальные свойства набора, но также использовать логическое значение false, возвращаемое set.add()при сбое добавления в набор.

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class randomUniqueNumberGenerator {

    public static final int SET_SIZE_REQUIRED = 10;
    public static final int NUMBER_RANGE = 100;

    public static void main(String[] args) {
        Random random = new Random();

        Set set = new HashSet<Integer>(SET_SIZE_REQUIRED);

        while(set.size()< SET_SIZE_REQUIRED) {
            while (set.add(random.nextInt(NUMBER_RANGE)) != true)
                ;
        }
        assert set.size() == SET_SIZE_REQUIRED;
        System.out.println(set);
    }
}

1
Хорошая идея. Однако важная отметка - если SET_SIZE_REQUIREDона достаточно велика (скажем, больше, чем NUMBER_RANGE / 2тогда, у вас гораздо большее ожидаемое время выполнения.
Ноамгот

5

Я сделал это вот так.

    Random random = new Random();
    ArrayList<Integer> arrayList = new ArrayList<Integer>();

    while (arrayList.size() < 6) { // how many numbers u need - it will 6
        int a = random.nextInt(49)+1; // this will give numbers between 1 and 50.

        if (!arrayList.contains(a)) {
            arrayList.add(a);
        }
    }

4

Это будет работать для генерации уникальных случайных чисел ................

import java.util.HashSet;
import java.util.Random;

public class RandomExample {

    public static void main(String[] args) {
        Random rand = new Random();
        int e;
        int i;
        int g = 10;
        HashSet<Integer> randomNumbers = new HashSet<Integer>();

        for (i = 0; i < g; i++) {
            e = rand.nextInt(20);
            randomNumbers.add(e);
            if (randomNumbers.size() <= 10) {
                if (randomNumbers.size() == 10) {
                    g = 10;
                }
                g++;
                randomNumbers.add(e);
            }
        }
        System.out.println("Ten Unique random numbers from 1 to 20 are  : " + randomNumbers);
    }
}

3

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

Например, 2 является примитивным корневым модулем 101, что означает, что степени 2 по модулю 101 дают вам неповторяющуюся последовательность, которая видит каждое число от 1 до 100 включительно:

2^0 mod 101 = 1
2^1 mod 101 = 2
2^2 mod 101 = 4
...
2^50 mod 101 = 100
2^51 mod 101 = 99
2^52 mod 101 = 97
...
2^100 mod 101 = 1

В коде Java вы должны написать:

void randInts() {
int num=1;
for (int ii=0; ii<101; ii++) {
    System.out.println(num);
    num= (num*2) % 101;
    }
}

Найти примитивный корень для определенного модуля может быть непросто, но функция "primroot" в Maple сделает это за вас.


Это интересно, но как мы можем гарантировать, что сгенерированная последовательность случайна? Не похоже. Кажется очень детерминированным иметь 1,2,4,8,16, ... в начале последовательности.
h4nek

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

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

2

Я пришел сюда из другого вопроса, который дублировал этот вопрос ( создание уникального случайного числа в java )

  1. Храните от 1 до 100 чисел в массиве.

  2. Сгенерировать случайное число от 1 до 100 в качестве позиции и вернуть массив [позиция-1], чтобы получить значение

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

  4. Если значение в массиве равно -1, снова получите случайное число, чтобы получить новое местоположение в массиве.


2

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

for(int i=0;i<4;i++)
        {
            rn[i]= GenerateRandomNumber();
            for (int j=0;j<i;j++)
            {
                if (rn[i] == rn[j])
                {
                    i--;
                }
            }
        }

вы можете оптимизировать, выполнив break;послеi—;
января,

0

попробуйте это

public class RandomValueGenerator {
    /**
     * 
     */
    private volatile List<Double> previousGenValues = new ArrayList<Double>();

    public void init() {
        previousGenValues.add(Double.valueOf(0));
    }

    public String getNextValue() {
        Random random = new Random();
        double nextValue=0;
        while(previousGenValues.contains(Double.valueOf(nextValue))) {
            nextValue = random.nextDouble();
        }
        previousGenValues.add(Double.valueOf(nextValue));
        return String.valueOf(nextValue);
    }
}

0

Это не сильно отличается от других ответов, но в конце мне нужен массив целых чисел:

    Integer[] indices = new Integer[n];
    Arrays.setAll(indices, i -> i);
    Collections.shuffle(Arrays.asList(indices));
    return Arrays.stream(indices).mapToInt(Integer::intValue).toArray();

0

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

package study;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/*
Created By Sachin  Rane on Jul 18, 2018
*/
public class UniqueRandomNumber {
    static Boolean[] boolArray;
    public static void main(String s[]){
        List<Integer> integers = new ArrayList<>();


        for (int i = 0; i < 10; i++) {
            integers.add(i);
        }


        //get unique random numbers
        boolArray = new Boolean[integers.size()+1];
        Arrays.fill(boolArray, false);
        for (int i = 0; i < 10; i++) {
            System.out.print(getUniqueRandomNumber(integers) + " ");

        }

    }

    private static int  getUniqueRandomNumber(List<Integer> integers) {
        int randNum =(int) (Math.random()*integers.size());
        if(boolArray[randNum]){
            while(boolArray[randNum]){
                randNum++;
                if(randNum>boolArray.length){
                    randNum=0;
                }
            }
            boolArray[randNum]=true;
            return randNum;
        }else {
            boolArray[randNum]=true;
            return randNum;
        }

    }

}

0

Выберите n уникальных случайных чисел от 0 до m-1.

int[] uniqueRand(int n, int m){
    Random rand = new Random();
    int[] r = new int[n];
    int[] result = new int[n];
    for(int i = 0; i < n; i++){
        r[i] = rand.nextInt(m-i);
        result[i] = r[i];
        for(int j = i-1; j >= 0; j--){
            if(result[i] >= r[j])
                result[i]++;
        }
    }
    return result;
}

Представьте себе список, содержащий числа от 0 до m-1. Чтобы выбрать первое число, мы просто используем rand.nextInt(m). Затем удалите номер из списка. Теперь осталось m-1 номеров, поэтому звоним rand.nextInt(m-1). Число, которое мы получаем, представляет позицию в списке. Если оно меньше первого числа, то это второе число, поскольку часть списка перед первым числом не была изменена удалением первого числа. Если позиция больше или равна первому числу, второе число равно позиции + 1. Сделайте некоторые дальнейшие выводы, вы можете получить этот алгоритм.

Объяснение

Этот алгоритм имеет сложность O (n ^ 2). Так что он хорош для создания небольшого количества уникальных чисел из большого набора. В то время как алгоритм на основе перемешивания требует как минимум O (m) для выполнения перемешивания.

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


0

Вы можете использовать класс Collections.

Служебный класс под названием Collections предлагает различные действия, которые могут быть выполнены с коллекцией, например ArrayList (например, поиск элементов, поиск максимального или минимального элемента, изменение порядка элементов и т. Д.). Одно из действий, которое он может выполнить, - это перемешать элементы. В случайном порядке каждый элемент случайным образом перемещается в другую позицию в списке. Это делается с помощью объекта Random. Это означает, что это детерминированная случайность, но так будет в большинстве ситуаций.

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

import java.util.Collections;
import java.util.ArrayList;
public class Lottery {
public static void main(String[] args) {
//define ArrayList to hold Integer objects
ArrayList numbers = new ArrayList();
for(int i = 0; i < 100; i++)
{
numbers.add(i+1);
}
Collections.shuffle(numbers);
System.out.println(numbers);
}
}

0

Хотя это старый поток, но добавление другого варианта может не повредить. (Лямбда-функции JDK 1.8, кажется, упрощают эту задачу);

Проблема может быть разбита на следующие шаги;

  • Получите минимальное значение для предоставленного списка целых чисел (для которого нужно генерировать уникальные случайные числа)
  • Получить максимальное значение для предоставленного списка целых чисел
  • Используйте класс ThreadLocalRandom (из JDK 1.8) для генерации случайных целочисленных значений против ранее найденных минимальных и максимальных целочисленных значений, а затем выполните фильтрацию, чтобы гарантировать, что значения действительно содержатся в первоначально предоставленном списке. Наконец, примените отличное к intstream, чтобы убедиться, что сгенерированные числа уникальны.

Вот функция с некоторым описанием:

/**
 * Provided an unsequenced / sequenced list of integers, the function returns unique random IDs as defined by the parameter
 * @param numberToGenerate
 * @param idList
 * @return List of unique random integer values from the provided list
 */
private List<Integer> getUniqueRandomInts(List<Integer> idList, Integer numberToGenerate) {

    List<Integer> generatedUniqueIds = new ArrayList<>();

    Integer minId = idList.stream().mapToInt (v->v).min().orElseThrow(NoSuchElementException::new);
    Integer maxId = idList.stream().mapToInt (v->v).max().orElseThrow(NoSuchElementException::new);

            ThreadLocalRandom.current().ints(minId,maxId)
            .filter(e->idList.contains(e))
            .distinct()
            .limit(numberToGenerate)
            .forEach(generatedUniqueIds:: add);

    return generatedUniqueIds;

}

Итак, чтобы получить 11 уникальных случайных чисел для объекта списка allIntegers, мы вызовем функцию вида:

    List<Integer> ids = getUniqueRandomInts(allIntegers,11);

Функция объявляет новый список arrayList «generatedUniqueIds» и заполняет его каждым уникальным случайным целым числом до требуемого числа перед возвратом.

Класс PS ThreadLocalRandom избегает общего начального значения в случае параллельных потоков.


0

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

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

  1. Сгенерируйте случайное число и добавьте его в новый массив.
  2. Сгенерируйте другое случайное число и проверьте, сохранено ли оно уже в новом массиве.
  3. Если нет, то добавьте и продолжайте
  4. иначе повторите шаг.
ArrayList<Integer> sampleList = new ArrayList<>();
sampleList.add(1);
sampleList.add(2);
sampleList.add(3);
sampleList.add(4);
sampleList.add(5);
sampleList.add(6);
sampleList.add(7);
sampleList.add(8);

Теперь sampleListмы получим пять уникальных случайных чисел.

int n;
randomList = new ArrayList<>();
for(int  i=0;i<5;i++){
    Random random = new Random();
    n=random.nextInt(8);     //Generate a random index between 0-7

    if(!randomList.contains(sampleList.get(n)))
    randomList.add(sampleList.get(n));
    else
        i--;    //reiterating the step
}
        

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

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


-1

Вы можете сгенерировать n уникальных случайных чисел от 0 до n-1 в java

public static void RandomGenerate(int n)
{
     Set<Integer> st=new HashSet<Integer>();
     Random r=new Random();
     while(st.size()<n)
     {
        st.add(r.nextInt(n));
     }

}


-2

Проверь это

public class RandomNumbers {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int n = 5;
        int A[] = uniqueRandomArray(n);
        for(int i = 0; i<n; i++){
            System.out.println(A[i]);
        }
    }
    public static int[] uniqueRandomArray(int n){
        int [] A = new int[n];
        for(int i = 0; i< A.length; ){
            if(i == A.length){
                break;
            }
            int b = (int)(Math.random() *n) + 1;
            if(f(A,b) == false){
                A[i++] = b;
            } 
        }
        return A;
    }
    public static boolean f(int[] A, int n){
        for(int i=0; i<A.length; i++){
            if(A[i] == n){
                return true;
            }
        }
        return false;
    }
}

2
Выбрасывая стандарты Java, удобочитаемость и удобство использования, а?
Остин Вернли,

Код - это не ответ .. Вы пишете ответ, а затем добавляете код, чтобы объяснить, что вы хотели.
Aditya

-2

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

public int GenerateRandomNo()
{
    int _min = 0000;
    int _max = 9999;
    Random _rdm = new Random();
    return _rdm.Next(_min, _max);
}
public int rand_num()
{
    randnum = GenerateRandomNo();
    string createText = randnum.ToString() + Environment.NewLine;
    string file_path = System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath) + @"\Invoices\numbers.txt";
    File.AppendAllText(file_path, createText);
    int number = File.ReadLines(file_path).Count(); //count number of lines in file
    System.IO.StreamReader file = new System.IO.StreamReader(file_path);
    do
    {
        randnum = GenerateRandomNo();
    }
    while ((file.ReadLine()) == randnum.ToString());
    file.Close();
    return randnum;

}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.