Generowanie unikalnych liczb losowych w Javie


88

Próbuję uzyskać liczby losowe z zakresu od 0 do 100. Ale chcę, aby były unikalne, a nie powtarzane w sekwencji. Na przykład, jeśli mam 5 liczb, powinny to być 82,12,53,64,32, a nie 82,12,53,12,32. Użyłem tego, ale generuje te same liczby w sekwencji.

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

5
Możesz stworzyć losową permutację zakresu 1..100(są do tego znane algorytmy), ale zatrzymaj się po określeniu pierwszych nelementów.
Kerrek SB



Odpowiedzi:


145
  • Dodaj kolejno każdą liczbę z zakresu w strukturze listy .
  • Potasuj to.
  • Zrób pierwsze „n”.

Oto prosta implementacja. Spowoduje to wydrukowanie 3 unikalnych liczb losowych z zakresu 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));
        }
    }
}

Pierwsza część poprawki z oryginalnym podejściem, jak wskazał Mark Byers w usuniętej odpowiedzi, polega na użyciu tylko jednej Randominstancji.

To właśnie powoduje, że liczby są identyczne. Wystąpienie Randomjest zapełniane przez bieżący czas w milisekundach. Dla określonej wartości początkowej instancja „losowa” zwróci dokładnie tę samą sekwencję liczb pseudolosowych .

UWAGA, że public Integer​(int value)konstruktor jest deprecatedod wersji Java 9.

Pierwszą pętlę for można po prostu zmienić na:

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

3
+1 za wskazanie jednej losowej instancji i udzielenie odpowiedzi na pytanie. :)
Mark Byers

Nie musisz tasować całego zakresu. Jeśli chcesz mieć n unikalnych liczb, musisz tylko przetasować pierwszą pozycję n, używając tasowania Fishera-Yatesa. Może to pomóc w przypadku dużej listy i małego n.
Rossum

59

W Javie 8+ możesz użyć intsmetody, Randomaby uzyskać IntStreamlosowe wartości distincti limitzredukować strumień do wielu unikalnych losowych wartości.

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

Randomma również metody, które tworzą LongStreams i DoubleStreams, jeśli ich potrzebujesz.

Jeśli chcesz wszystkie (lub dużą liczbę) liczb w zakresie w kolejności losowej, bardziej wydajne może być dodanie wszystkich liczb do listy, przetasowanie ich i pobranie pierwszego n, ponieważ powyższy przykład jest obecnie zaimplementowany generując liczby losowe z żądanego zakresu i przepuszczając je przez zbiór (podobnie jak w przypadku odpowiedzi Roba Kielty'ego ), co może wymagać wygenerowania o wiele więcej niż kwota przekazana do limitu, ponieważ prawdopodobieństwo wygenerowania nowej unikalnej liczby maleje wraz z każdym znalezionym. Oto przykład innego sposobu:

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

Potrzebowałem tego dla jakiegoś kodu, który testuję, i Arrays#setAll()jest trochę szybszy niż strumień. A więc: `Integer [] indeksy = new Integer [n]; Arrays.setAll (indeksy, i -> i); Collections.shuffle (Arrays.asList (indeksy)); return Arrays.stream (indeksy) .mapToInt (Integer :: intValue) .toArray (); `
AbuNassar,

18
  1. Utwórz tablicę ze 100 liczb, a następnie losuj ich kolejność.
  2. Opracuj generator liczb pseudolosowych o zakresie 100.
  3. Utwórz tablicę logiczną zawierającą 100 elementów, a następnie ustaw element na wartość true, gdy wybierzesz tę liczbę. Po wybraniu następnej liczby sprawdź tablicę i spróbuj ponownie, jeśli element tablicy jest ustawiony. (Możesz utworzyć łatwą do wyczyszczenia tablicę logiczną z tablicą, w longktórej przesuwasz i maskujesz, aby uzyskać dostęp do poszczególnych bitów).

2
+1 za alternatywne podejście; pick()jest przykładem.
trashgod

1
Zamiast używać tablicy boolowskiej, możesz użyć a HashSet, gdzie przechowujesz już wygenerowane liczby i używasz containsdo sprawdzenia, czy już wygenerowałeś tę liczbę. HashSetPrawdopodobnie będzie nieco wolniejszy niż logicznej tablicy, ale zajmują mniej miejsca w pamięci.
Rory O'Kane

1
@ RoryO'Kane - Jestem prawie pewien, że tablica boolowska zajęłaby mniej miejsca, gdyby została zaimplementowana jako tablica long [2]. Nie ma mowy, żebyś mógł zrobić tak mały zestaw HashSet.
Hot Licks

Ostatnie podejście jest trochę brzydkie, ponieważ nie miałoby dobrze zdefiniowanej liczby kroków do wygenerowania całej sekwencji. Nie musisz też wymyślać koła na nowo - BitSet .
Pavel Horal

16

Użyj Collections.shuffle()na wszystkich 100 liczbach i wybierz pierwsze pięć, jak pokazano tutaj .


13

Uważam, że warto wspomnieć o tej metodzie.

   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

Ponownie rozlokowałem odpowiedź Ananda, aby wykorzystać nie tylko unikalne właściwości zestawu, ale także użyć wartości logicznej false zwracanej przez, set.add()gdy dodanie do zestawu nie powiedzie się.

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
Dobry pomysł. Ważny znak - jeśli SET_SIZE_REQUIREDjest wystarczająco duży (powiedzmy, więcej niż NUMBER_RANGE / 2wtedy, masz znacznie większy oczekiwany czas pracy.
noamgot

5

Zrobiłem to w ten sposób.

    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

To zadziała w celu wygenerowania unikalnych liczb losowych ................

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

Sprytnym sposobem na to jest użycie wykładników elementu pierwotnego w module.

Na przykład 2 to prymitywny rdzeń mod 101, co oznacza, że ​​potęgi 2 mod 101 dają niepowtarzalną sekwencję, która widzi każdą liczbę od 1 do 100 włącznie:

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

W kodzie Java napisałbyś:

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

Znalezienie prymitywnego korzenia dla określonego modułu może być trudne, ale funkcja „primroot” Maple'a zrobi to za Ciebie.


To ciekawe, ale jak możemy zapewnić, że generowana sekwencja jest losowa? Nie wydaje się. Wydaje się bardzo deterministyczne, aby mieć 1,2,4,8,16, ... na początku sekwencji.
h4nek

To nie jest przypadkowe ... to pseudolosowe. Nikt nie wie, jak wygenerować prawdziwie losowe liczby. Jeśli nie podoba ci się początkowy wzór, możesz użyć większej podstawy jako prymitywnego korzenia.
AT - uczeń

Pseudolosowe byłoby w porządku. Ale tutaj, dla danego „zakresu”, liczba pierwiastków pierwotnych, a tym samym unikalnych sekwencji jest ograniczona, szczególnie dla mniejszych zakresów. Wydaje się więc, że jest problem z wzorcem, np. Zawsze posiadającym podciąg potęgi pierwiastka. I nie uzyskiwanie (prawdopodobnie) bardzo różnych sekwencji w wielu przebiegach, chyba że zastosujemy więcej sztuczek. Myślę, że to zależy od przypadku użycia. Zmiana bazy i tak jest fajnym ulepszeniem, choć tylko „przesuwa” wzór.
h4nek

2

Przyszedłem tutaj z innego pytania, które było duplikatem tego pytania ( Generowanie unikalnej liczby losowej w java )

  1. Przechowuj od 1 do 100 liczb w tablicy.

  2. Wygeneruj liczbę losową z przedziału od 1 do 100 jako pozycję i zwróć tablicę [pozycja-1], aby uzyskać wartość

  3. Gdy użyjesz liczby w tablicy, oznacz wartość jako -1 (nie ma potrzeby utrzymywania innej tablicy, aby sprawdzić, czy ta liczba jest już używana)

  4. Jeśli wartością w tablicy jest -1, pobierz losową liczbę ponownie, aby pobrać nową lokalizację w tablicy.


2

Mam proste rozwiązanie tego problemu, dzięki temu możemy łatwo wygenerować n liczby unikalnych liczb losowych, to po prostu logika, którą każdy może używać w dowolnym języku.

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

można zoptymalizować wykonując break;poi—;
Jan

0

wypróbuj to

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

Nie różni się to znacząco od innych odpowiedzi, ale na końcu chciałem mieć tablicę liczb całkowitych:

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

0

możesz użyć tablicy boolowskiej, aby wypełnić prawdę, jeśli wartość została pobrana w przeciwnym razie ustaw nawigację po tablicy boolowskiej, aby uzyskać wartość zgodnie z poniższym

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

Wybierz n niepowtarzalnych liczb losowych od 0 do 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;
}

Wyobraź sobie listę zawierającą liczby od 0 do m-1. Aby wybrać pierwszą liczbę, po prostu używamy rand.nextInt(m). Następnie usuń numer z listy. Teraz pozostaje liczba m-1, więc dzwonimy rand.nextInt(m-1). Otrzymana przez nas liczba reprezentuje pozycję na liście. Jeśli jest mniejsza niż pierwsza liczba, to jest to druga liczba, ponieważ część listy poprzedzająca pierwszą liczbę nie została zmieniona przez usunięcie pierwszej liczby. Jeśli pozycja jest większa lub równa pierwszej liczbie, druga liczba to pozycja + 1. Wykonaj dalsze wyprowadzenie, możesz uzyskać ten algorytm.

Wyjaśnienie

Ten algorytm ma złożoność O (n ^ 2). Jest więc dobry do generowania małej ilości unikalnych liczb z dużego zbioru. Podczas gdy algorytm oparty na tasowaniu potrzebuje co najmniej O (m), aby wykonać shuffle.

Również algorytm oparty na tasowaniu potrzebuje pamięci do przechowywania każdego możliwego wyniku, aby wykonać tasowanie, ten algorytm nie potrzebuje.


0

Możesz użyć klasy Collections.

Klasa narzędziowa o nazwie Kolekcje oferuje różne akcje, które można wykonać na kolekcji, takiej jak ArrayList (np. Przeszukiwanie elementów, znajdowanie elementu maksymalnego lub minimalnego, odwracanie kolejności elementów itd.). Jedną z czynności, które może wykonać, jest tasowanie elementów. Tasowanie spowoduje losowe przeniesienie każdego elementu na inne miejsce na liście. Robi to za pomocą obiektu Random. Oznacza to, że jest to deterministyczna losowość, ale w większości sytuacji wystarczy.

Aby przetasować ArrayList, dodaj import kolekcji na początku programu, a następnie użyj statycznej metody Shuffle. Wymaga tasowania ArrayList jako parametru:

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

Chociaż to stary wątek, ale dodanie innej opcji może nie zaszkodzić. (Funkcje lambda JDK 1.8 wydają się to ułatwiać);

Problem można podzielić na następujące kroki;

  • Uzyskaj minimalną wartość dla podanej listy liczb całkowitych (dla których generowane są unikalne liczby losowe)
  • Uzyskaj maksymalną wartość dla podanej listy liczb całkowitych
  • Użyj klasy ThreadLocalRandom (z JDK 1.8), aby wygenerować losowe wartości całkowite na podstawie wcześniej znalezionych minimalnych i maksymalnych wartości całkowitych, a następnie zastosuj filtr, aby upewnić się, że wartości są rzeczywiście zawarte na pierwotnie dostarczonej liście. Na koniec zastosuj odrębne do strumienia wewnętrznego, aby zapewnić, że wygenerowane liczby są niepowtarzalne.

Oto funkcja z pewnym opisem:

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

}

Aby uzyskać 11 unikalnych liczb losowych dla obiektu listy „allIntegers”, wywołamy funkcję w następujący sposób;

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

Funkcja deklaruje new arrayList „generatedUniqueIds” i przed zwróceniem wypełnia każdą unikalną losową liczbę całkowitą do wymaganej liczby.

Klasa PS ThreadLocalRandom unika wspólnej wartości inicjatora w przypadku współbieżnych wątków.


0

Jest to najprostsza metoda generowania unikatowych wartości losowych w zakresie lub z tablicy .

W tym przykładzie będę używał predefiniowanej tablicy, ale możesz dostosować tę metodę również do generowania liczb losowych. Najpierw utworzymy przykładową tablicę, z której będą pobierane nasze dane.

  1. Wygeneruj liczbę losową i dodaj ją do nowej tablicy.
  2. Wygeneruj kolejną liczbę losową i sprawdź, czy jest już zapisana w nowej tablicy.
  3. Jeśli nie, dodaj go i kontynuuj
  4. w przeciwnym razie powtórz krok.
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);

Teraz sampleListstworzymy pięć liczb losowych, które są niepowtarzalne.

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
}
        

Jest to koncepcyjnie bardzo proste. Jeśli wygenerowana wartość losowa już istnieje, powtórzymy krok. Będzie to trwało, dopóki wszystkie wygenerowane wartości nie będą unikalne.

Jeśli uznasz tę odpowiedź za przydatną, możesz ją zagłosować, ponieważ jest o wiele prosta w koncepcji w porównaniu z innymi odpowiedziami .


-1

W Javie możesz wygenerować n niepowtarzalnych liczb losowych z zakresu od 0 do n-1

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

Sprawdź to

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
Wyrzucanie standardów Java, czytelności i użyteczności przez okno, co?
austin wernli

Kod nie jest odpowiedzią. Piszesz odpowiedź, a następnie dodajesz kod, aby wyjaśnić, czego chciałeś.
Aditya

-2

Poniżej znajduje się sposób, w jaki zawsze generowałem unikalny numer. Funkcja losowa generuje liczbę i przechowuje ją w pliku tekstowym, a następnym razem, gdy sprawdza ją w pliku, porównuje ją i generuje nowy unikalny numer, w ten sposób zawsze jest nowy unikalny numer.

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;

}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.