Генерування унікальних випадкових чисел у 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
  • Додайте кожне число в діапазоні послідовно в структуру списку .
  • Перемішайте .
  • Візьміть перше 'n'.

Ось проста реалізація. Буде надруковано 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Примірник висівають на поточний момент часу в мілісекундах. Для певного насіннєвого значення екземпляр 'random' поверне ту саму послідовність псевдо випадкових чисел.

Зверніть увагу, що public Integer​(int value)конструктор працює deprecatedз Java 9.

Перший цикл for можна просто змінити на:

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

3
+1 за вказівку на один випадковий приклад та відповідь на запитання. :)
Марк Байерс

Вам не потрібно перетасовувати весь асортимент. Якщо вам потрібно n унікальних чисел, то вам потрібно лише перетасувати першу n позицію за допомогою перетасовки Fisher-Yates. Це може допомогти з великим списком і малим n.
rossum

59

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

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

Randomтакож має методи, які створюють LongStreams і DoubleStreams, якщо вони вам потрібні.

Якщо ви хочете, щоб усі (або велика кількість) чисел у діапазоні були у випадковому порядку, можливо, було б ефективніше додати всі числа до списку, перемішати його та взяти перше 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 [] індекси = new Integer [n]; Arrays.setAll (індекси, i -> i); Collections.shuffle (Arrays.asList (індекси)); повернути Arrays.stream (індекси) .mapToInt (Integer :: intValue) .toArray (); `
АбуНассар,

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

2
+1 для альтернативного підходу; pick()є прикладом.
trashgod

1
Замість того, щоб використовувати логічний масив, ви можете використовувати a 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тоді, ви отримаєте набагато більший очікуваний час роботи.
noamgot

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;
    }
}

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


Це цікаво, але як ми могли б переконатися, що згенерована послідовність є випадковою? Здається, не так. Здається, дуже детермінованим є наявність 1,2,4,8,16, ... на початку послідовності.
h4nek

Це не випадково ... це псевдовипадково. Ніхто не знає, як генерувати справді випадкові числа. Якщо вам не подобається початковий шаблон, ви можете використовувати більшу основу як примітивний корінь.
AT - студент

Псевдовипадково було б добре. Але тут, для даного "діапазону", кількість примітивних коренів і, отже, унікальних послідовностей обмежена, особливо для менших діапазонів .. Отже, існує проблема із шаблоном, наприклад, завжди має послідовність ступенів кореня. І не отримати (ймовірно) дуже різну послідовність під час кількох прогонів, якщо ми не застосуємо ще кілька хитрощів. Я думаю, це залежить від випадку використання. Зміна бази в будь-якому випадку є приємним оновленням, хоча воно лише «змінює» шаблон.
h4nek

2

Я прийшов сюди з іншого питання, яке було продубльовано цим запитанням ( Створення унікального випадкового числа в Java )

  1. Зберігайте від 1 до 100 чисел у масиві.

  2. Створіть випадкове число від 1 до 100 як позицію та поверніть масив [position-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—;
січня,

1

Хоча це стара тема, але додавання іншого варіанту може не зашкодити. (Здається, лямбда-функції JDK 1.8 полегшують це);

Проблему можна розбити на наступні кроки;

  • Отримайте мінімальне значення для наданого списку цілих чисел (для яких потрібно генерувати унікальні випадкові числа)
  • Отримайте максимальне значення для наданого списку цілих чисел
  • Використовуйте клас ThreadLocalRandom (з JDK 1.8), щоб генерувати випадкові цілі значення відносно раніше знайдених мінімальних і максимальних цілих значень, а потім фільтрувати, щоб переконатися, що значення справді містяться у спочатку наданому списку. Нарешті, застосуйте відмінне значення до внутрішнього потоку, щоб переконатися, що згенеровані числа є унікальними.

Ось функція з деяким описом:

/**
 * 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

спробуйте це

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

Це найпростіший метод генерування унікальних випадкових значень у діапазоні або з масиву .

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

  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, читабельності та зручності в користування, так?
Остін Вернлі,

Код - це не відповідь .. Ви пишете відповідь, а потім додаєте код, щоб пояснити, що ви хотіли.
Адітя

-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.