Menghasilkan Angka Acak Unik di Java


90

Saya mencoba mendapatkan angka acak antara 0 dan 100. Tapi saya ingin angka itu unik, tidak diulang secara berurutan. Misalnya jika saya mendapat 5 angka, seharusnya 82,12,53,64,32 dan bukan 82,12,53,12,32 Saya menggunakan ini, tetapi menghasilkan angka yang sama secara berurutan.

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

5
Anda dapat membuat permutasi acak dari rentang 1..100(ada algoritme terkenal untuk itu), tetapi berhenti setelah Anda menentukan nelemen pertama .
Kerrek SB



Jawaban:


146
  • Tambahkan setiap angka dalam rentang secara berurutan dalam struktur daftar .
  • Kocok .
  • Ambil 'n' pertama.

Berikut adalah implementasi sederhana. Ini akan mencetak 3 nomor acak unik dari kisaran 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));
        }
    }
}

Bagian pertama dari perbaikan dengan pendekatan asli, seperti yang ditunjukkan Mark Byers dalam jawaban yang sekarang dihapus, adalah menggunakan hanya satu Randomcontoh.

Itulah yang menyebabkan angkanya menjadi identik. Sebuah Randominstance diunggulkan oleh waktu saat ini dalam milidetik. Untuk nilai seed tertentu , instance 'random' akan mengembalikan urutan bilangan acak semu yang sama persis .

Perhatikan bahwa public Integer​(int value)konstruktornya adalah deprecatedsejak Java 9.

Perulangan for pertama dapat dengan mudah diubah menjadi:

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

3
1 untuk menunjukkan contoh acak tunggal dan menjawab pertanyaan. :)
Mark Byers

Anda tidak perlu mengacak seluruh rentang. Jika Anda menginginkan n nomor unik, maka Anda hanya perlu mengocok n posisi pertama menggunakan pengacakan Fisher-Yates. Ini dapat membantu dengan daftar besar dan n kecil.
rossum

62

Dengan Java 8+ Anda dapat menggunakan intsmetode Randomuntuk mendapatkan IntStreamnilai acak kemudian distinctdan limituntuk mengurangi aliran ke sejumlah nilai acak unik.

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

Randomjuga memiliki metode yang membuat LongStreams dan DoubleStreams jika Anda membutuhkannya.

Jika Anda ingin semua (atau sejumlah besar) angka dalam rentang dalam urutan acak, mungkin lebih efisien untuk menambahkan semua angka ke daftar, mengocoknya, dan mengambil n pertama karena contoh di atas sedang diterapkan dengan menghasilkan nomor acak dalam kisaran yang diminta dan meneruskannya melalui satu set (mirip dengan jawaban Rob Kielty ), yang mungkin memerlukan menghasilkan lebih banyak dari jumlah yang diteruskan untuk membatasi karena kemungkinan menghasilkan nomor unik baru menurun dengan setiap yang ditemukan. Berikut contoh cara lain:

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

Saya memerlukan ini untuk beberapa kode yang saya tolak, dan Arrays#setAll()sedikit lebih cepat daripada streaming. Jadi: `Integer [] indices = new Integer [n]; Arrays.setAll (indeks, i -> i); Collections.shuffle (Arrays.asList (indeks)); return Arrays.stream (indeks) .mapToInt (Integer :: intValue) .toArray (); `
AbuNassar

18
  1. Buat larik 100 angka, lalu acak urutannya.
  2. Rancang generator bilangan acak semu yang memiliki jangkauan 100.
  3. Buat larik boolean yang terdiri dari 100 elemen, lalu setel elemen menjadi true saat Anda memilih nomor itu. Ketika Anda memilih nomor berikutnya, periksa array dan coba lagi jika elemen array disetel. (Anda dapat membuat larik boolean yang mudah dibersihkan dengan larik longtempat Anda menggeser dan menutupi untuk mengakses bit individual.)

2
1 untuk pendekatan alternatif; pick()adalah sebuah contoh.
tong sampah

1
Alih-alih menggunakan array boolean, Anda dapat menggunakan a HashSet, tempat Anda menyimpan angka yang telah Anda buat dan gunakan containsuntuk menguji apakah Anda sudah menghasilkan angka itu. Ini HashSetmungkin akan sedikit lebih lambat dari array boolean, tetapi membutuhkan lebih sedikit memori.
Rory O'Kane

1
@ RoryO'Kane - Saya cukup yakin array boolean akan memakan lebih sedikit ruang, jika diimplementasikan sebagai array panjang [2]. Tidak mungkin Anda bisa membuat HashSet sekecil itu.
Hot Licks

Pendekatan terakhir agak jelek karena tidak memiliki jumlah langkah yang ditentukan dengan baik untuk menghasilkan seluruh urutan. Anda juga tidak perlu menemukan kembali roda - BitSet .
Pavel Horal

16

Gunakan Collections.shuffle()pada semua 100 nomor dan pilih lima yang pertama, seperti yang ditunjukkan di sini .


13

Saya merasa metode ini layak untuk disebutkan.

   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

Saya memfaktorkan ulang jawaban Anand untuk menggunakan tidak hanya properti unik dari sebuah Set tetapi juga menggunakan boolean false yang dikembalikan oleh set.add()saat penambahan ke set gagal.

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
Ide bagus. Sebuah tanda yang penting - jika SET_SIZE_REQUIREDcukup besar (katakanlah, lebih dari NUMBER_RANGE / 2itu Anda mendapatkan waktu kerja yang diharapkan jauh lebih besar.
noamgot

5

Saya telah membuat ini seperti itu.

    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

Ini akan bekerja untuk menghasilkan nomor acak unik ................

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

Salah satu cara cerdas untuk melakukannya adalah dengan menggunakan eksponen elemen primitif dalam modulus.

Misalnya, 2 adalah root mod 101 primitif, yang berarti bahwa pangkat dari 2 mod 101 memberi Anda urutan yang tidak berulang yang melihat setiap angka dari 1 hingga 100 inklusif:

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

Dalam kode Java, Anda akan menulis:

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

Menemukan root primitif untuk modulus tertentu bisa jadi rumit, tetapi fungsi "primroot" Maple akan melakukannya untuk Anda.


Itu menarik, tetapi bagaimana kita memastikan bahwa urutan yang dihasilkan adalah acak? Sepertinya tidak. Tampaknya sangat deterministik untuk memiliki 1,2,4,8,16, ... di awal sebuah urutan.
h4nek

Itu tidak acak ... itu pseudo-random. Tidak ada yang tahu bagaimana menghasilkan angka yang benar-benar acak. Jika Anda tidak menyukai pola awal, Anda dapat menggunakan basis yang lebih besar sebagai akar primitif.
AT - pelajar

Pseudo-random akan baik-baik saja. Tetapi di sini, untuk "rentang" tertentu, jumlah akar primitif dan oleh karena itu urutan unik dibatasi, terutama untuk rentang yang lebih kecil .. Jadi tampaknya ada masalah dengan pola tersebut, misalnya selalu memiliki urutan pangkat dari akar. Dan tidak mendapatkan (mungkin) urutan yang sangat berbeda pada beberapa proses, kecuali kita menerapkan beberapa kesalahan lagi. Saya kira itu tergantung pada kasus penggunaan. Mengubah basis adalah peningkatan yang bagus, meskipun itu hanya "menggeser" pola.
h4nek

2

Saya datang ke sini dari pertanyaan lain, yang merupakan duplikat dari pertanyaan ini ( Menghasilkan nomor acak unik di java )

  1. Simpan 1 hingga 100 angka dalam Array.

  2. Hasilkan angka acak antara 1 hingga 100 sebagai posisi dan kembalikan larik [posisi-1] untuk mendapatkan nilainya

  3. Setelah Anda menggunakan angka dalam larik, tandai nilainya sebagai -1 (Tidak perlu mempertahankan larik lain untuk memeriksa apakah nomor ini sudah digunakan)

  4. Jika nilai dalam array -1, dapatkan kembali nomor acak untuk mengambil lokasi baru dalam array.


2

Saya punya solusi mudah untuk masalah ini, Dengan ini kita dapat dengan mudah menghasilkan n sejumlah angka acak unik, Logikanya siapa pun dapat menggunakannya dalam bahasa apa pun.

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

Anda dapat mengoptimalkan dengan melakukan break;setelahi—;
Jan

1

Meskipun ini adalah utas lama, tetapi menambahkan opsi lain mungkin tidak membahayakan. (Fungsi lambda JDK 1.8 tampaknya membuatnya mudah);

Masalahnya bisa dipecah menjadi langkah-langkah berikut;

  • Dapatkan nilai minimum untuk daftar bilangan bulat yang disediakan (yang akan menghasilkan nomor acak unik)
  • Dapatkan nilai maksimum untuk daftar bilangan bulat yang disediakan
  • Gunakan kelas ThreadLocalRandom (dari JDK 1.8) untuk menghasilkan nilai bilangan bulat acak terhadap nilai bilangan bulat min dan maks yang ditemukan sebelumnya, lalu filter untuk memastikan bahwa nilai tersebut benar-benar terdapat dalam daftar yang disediakan semula. Akhirnya berlaku berbeda untuk intstream untuk memastikan bahwa nomor yang dihasilkan unik.

Berikut adalah fungsi dengan beberapa deskripsi:

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

}

Sehingga, untuk mendapatkan 11 nomor acak unik untuk objek daftar 'allIntegers', kita akan memanggil fungsi seperti;

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

Fungsi ini mendeklarasikan arrayList baru 'generatedUniqueIds' dan mengisi dengan setiap bilangan bulat acak unik hingga jumlah yang diperlukan sebelum kembali.

Kelas PS ThreadLocalRandom menghindari nilai benih yang sama dalam kasus utas bersamaan.


0

coba ini

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

Ini tidak berbeda secara signifikan dari jawaban lain, tetapi saya ingin array bilangan bulat pada akhirnya:

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

0

Anda dapat menggunakan array boolean untuk mengisi true jika nilai diambil, setel navigasi melalui array boolean untuk mendapatkan nilai seperti yang diberikan di bawah ini

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

Pilih n nomor acak unik dari 0 hingga 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;
}

Bayangkan sebuah daftar berisi angka dari 0 hingga m-1. Untuk memilih angka pertama, kita cukup menggunakan rand.nextInt(m). Kemudian hapus nomor tersebut dari daftar. Sekarang masih ada nomor m-1, jadi kita panggilrand.nextInt(m-1) . Nomor yang kami dapatkan mewakili posisi dalam daftar. Jika lebih kecil dari angka pertama, maka itu adalah angka kedua, karena bagian dari daftar sebelum angka pertama tidak diubah oleh penghapusan angka pertama. Jika posisinya lebih besar dari atau sama dengan angka pertama, angka kedua adalah posisi + 1. Lakukan beberapa derivasi lebih lanjut, Anda bisa mendapatkan algoritma ini.

Penjelasan

Algoritma ini memiliki kompleksitas O (n ^ 2). Jadi bagus untuk menghasilkan sejumlah kecil angka unik dari set besar. Sedangkan algoritma berbasis shuffle membutuhkan minimal O (m) untuk melakukan shuffle.

Juga algoritma berbasis shuffle membutuhkan memori untuk menyimpan setiap kemungkinan hasil untuk melakukan shuffle, algoritma ini tidak perlu.


0

Anda dapat menggunakan kelas Koleksi.

Kelas utilitas yang disebut Koleksi menawarkan tindakan berbeda yang dapat dilakukan pada koleksi seperti ArrayList (misalnya, mencari elemen, menemukan elemen maksimum atau minimum, membalik urutan elemen, dan seterusnya). Salah satu tindakan yang dapat dilakukan adalah mengocok elemen. Pengacakan akan memindahkan setiap elemen secara acak ke posisi berbeda dalam daftar. Ini dilakukan dengan menggunakan objek Acak. Ini berarti keacakan deterministik, tetapi akan berhasil dalam banyak situasi.

Untuk mengacak ArrayList, tambahkan impor Koleksi ke bagian atas program dan kemudian gunakan metode statis Shuffle. Diperlukan ArrayList untuk diacak sebagai parameter:

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

-1

Anda dapat menghasilkan n nomor acak unik antara 0 hingga n-1 di 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));
     }

}


-1

Ini adalah metode paling sederhana untuk menghasilkan nilai acak unik dalam rentang atau dari larik .

Dalam contoh ini, saya akan menggunakan array yang telah ditentukan tetapi Anda dapat menyesuaikan metode ini untuk menghasilkan angka acak juga. Pertama, kita akan membuat larik sampel untuk mengambil data kita.

  1. Hasilkan nomor acak dan tambahkan ke array baru.
  2. Hasilkan nomor acak lain dan periksa apakah sudah disimpan dalam array baru.
  3. Jika tidak, tambahkan dan lanjutkan
  4. jika tidak mengulangi langkah tersebut.
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);

Nah dari sampleListsitu kita akan menghasilkan lima nomor acak yang unik.

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
}
        

Ini secara konseptual sangat sederhana. Jika nilai acak yang dihasilkan sudah ada maka kami akan mengulangi langkah tersebut. Ini akan berlanjut sampai semua nilai yang dihasilkan unik.

Jika menurut Anda jawaban ini berguna, maka Anda dapat memilihnya karena konsepnya lebih sederhana dibandingkan dengan jawaban lainnya .


-2

Periksa ini

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
Membuang standar java, keterbacaan dan kegunaan di luar jendela eh?
austin wernli

Kode bukanlah jawaban .. Anda menulis jawaban, lalu Anda menambahkan kode untuk menjelaskan apa yang Anda inginkan.
Aditya

-2

Di bawah ini adalah cara saya selalu menghasilkan nomor unik. Fungsi acak menghasilkan angka dan menyimpannya dalam textfile kemudian lain kali memeriksanya dalam file membandingkannya dan menghasilkan nomor unik baru sehingga dengan cara ini selalu ada nomor unik baru.

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;

}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.