Gerando Números Aleatórios Únicos em Java


90

Estou tentando obter números aleatórios entre 0 e 100. Mas quero que sejam únicos, não repetidos em uma sequência. Por exemplo, se eu tenho 5 números, eles devem ser 82,12,53,64,32 e não 82,12,53,12,32 Eu usei isso, mas gera os mesmos números em uma sequência.

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

5
Você poderia criar uma permutação aleatória do intervalo 1..100(existem algoritmos famosos para isso), mas pare depois de determinar os primeiros nelementos.
Kerrek SB



Respostas:


146
  • Adicione cada número no intervalo sequencialmente em uma estrutura de lista .
  • Embaralhe .
  • Pegue o primeiro 'n'.

Aqui está uma implementação simples. Isso imprimirá 3 números aleatórios exclusivos no intervalo de 1 a 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));
        }
    }
}

A primeira parte da correção com a abordagem original, como Mark Byers apontou em uma resposta agora excluída, é usar apenas uma única Randominstância.

É isso que está fazendo com que os números sejam idênticos. Uma Randominstância é propagada pela hora atual em milissegundos. Para um determinado valor de semente, a instância 'aleatória' retornará exatamente a mesma sequência de números pseudo-aleatórios .

NOTE que o public Integer​(int value)construtor é deprecateddesde Java 9.

O primeiro loop for pode simplesmente ser alterado para:

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

3
1 para apontar uma única instância aleatória e responder à pergunta. :)
Mark Byers

Você não precisa embaralhar todo o intervalo. Se você quiser n números únicos, então você só precisa embaralhar a primeira posição n usando um embaralhamento de Fisher-Yates. Isso pode ajudar com uma lista grande e um pequeno n.
rossum

62

Com o Java 8+, você pode usar o intsmétodo de Randompara obter um IntStreamvalor aleatório distincte então limitreduzir o fluxo a um número de valores aleatórios exclusivos.

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

Randomtambém possui métodos que criam LongStreams e DoubleStreams se você precisar deles.

Se você quiser todos (ou uma grande quantidade) dos números em um intervalo em uma ordem aleatória, pode ser mais eficiente adicionar todos os números a uma lista, embaralhá-la e pegar o primeiro n porque o exemplo acima está implementado atualmente gerando números aleatórios no intervalo solicitado e passando-os por um conjunto (semelhante à resposta de Rob Kielty ), o que pode exigir a geração de muito mais do que a quantidade passada para o limite, pois a probabilidade de gerar um novo número exclusivo diminui com cada um deles encontrado. Aqui está um exemplo da outra maneira:

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

Eu precisava disso para algum código que estou testando e Arrays#setAll()é um pouco mais rápido do que um fluxo. Portanto: `Índices inteiros [] = novo inteiro [n]; Arrays.setAll (índices, i -> i); Collections.shuffle (Arrays.asList (índices)); return Arrays.stream (índices) .mapToInt (Integer :: intValue) .toArray (); `
AbuNassar

18
  1. Crie uma matriz de 100 números e, a seguir, randomize sua ordem.
  2. Desenvolva um gerador de números pseudo-aleatórios com um intervalo de 100.
  3. Crie uma matriz booleana de 100 elementos e, em seguida, defina um elemento verdadeiro ao escolher esse número. Ao escolher o próximo número, verifique a matriz e tente novamente se o elemento da matriz está definido. (Você pode fazer uma matriz booleana fácil de limpar com uma matriz de longonde você desloca e mascara para acessar bits individuais.)

2
+1 para a abordagem alternativa; pick()é um exemplo.
trashgod

1
Em vez de usar um array booleano, você pode usar a HashSet, onde armazena os números já gerados e usa containspara testar se já gerou esse número. A HashSetprovavelmente será um pouco mais lento do que uma matriz booleana, mas ocupam menos memória.
Rory O'Kane

1
@ RoryO'Kane - Tenho certeza que o array booleano ocuparia menos espaço, se implementado como um array de long [2]. De jeito nenhum você poderia fazer um HashSet tão pequeno.
Hot Licks

A última abordagem é um pouco feia, pois não teria um número bem definido de etapas para gerar toda a sequência. Além disso, você não precisa reinventar a roda - BitSet .
Pavel Horal

16

Use Collections.shuffle()em todos os 100 números e selecione os cinco primeiros, conforme mostrado aqui .


13

Acho que vale a pena mencionar esse método.

   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

Refleti a resposta de Anand para fazer uso não apenas das propriedades únicas de um conjunto, mas também usar o booleano falso retornado por set.add()quando uma adição ao conjunto falha.

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
Boa ideia. Porém, uma marca importante - se SET_SIZE_REQUIREDfor grande o suficiente (digamos, mais do que NUMBER_RANGE / 2então, você obteve um tempo de execução esperado muito maior.
noamgot

5

Eu fiz isso assim.

    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

Isso funcionará para gerar números aleatórios únicos ................

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

Uma maneira inteligente de fazer isso é usar expoentes de um elemento primitivo no módulo.

Por exemplo, 2 é uma raiz mod 101 primitiva, o que significa que os poderes de 2 mod 101 fornecem uma sequência não repetitiva que vê todos os números de 1 a 100, inclusive:

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

No código Java, você escreveria:

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

Encontrar uma raiz primitiva para um módulo específico pode ser complicado, mas a função "primroot" do Maple fará isso para você.


Isso é interessante, mas como podemos garantir que a sequência gerada seja aleatória? Não parece ser. Parece muito determinístico ter 1,2,4,8,16, ... no início de uma sequência.
h4nek

Não é aleatório ... é pseudo-aleatório. Ninguém sabe como gerar números verdadeiramente aleatórios. Se você não gostar do padrão inicial, pode usar uma base maior como raiz primitiva.
AT - aluno de

Pseudo-aleatório seria bom. Mas aqui, para um determinado "intervalo", a quantidade de raízes primitivas e, portanto, sequências únicas é limitada, especialmente para intervalos menores. Portanto, parece haver um problema com o padrão, por exemplo, sempre ter uma subsequência de poderes da raiz. E não obter uma sequência (provavelmente) muito diferente em várias execuções, a menos que apliquemos mais algumas travessuras. Acho que depende do caso de uso. Mudar a base é uma boa atualização de qualquer maneira, embora apenas "mude" o padrão.
h4nek

2

Eu vim aqui de outra pergunta, que é uma duplicata desta pergunta ( Gerando número aleatório único em java )

  1. Armazene de 1 a 100 números em uma matriz.

  2. Gere um número aleatório entre 1 a 100 como posição e retorna a matriz [posição 1] para obter o valor

  3. Depois de usar um número na matriz, marque o valor como -1 (não há necessidade de manter outra matriz para verificar se este número já está sendo usado)

  4. Se o valor na matriz for -1, obtenha o número aleatório novamente para buscar um novo local na matriz.


2

Eu tenho uma solução fácil para este problema, com isso podemos facilmente gerar n número de números aleatórios únicos, é lógico que qualquer um pode usá-lo em qualquer linguagem.

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

você pode otimizar fazendo um break;apósi—;
janeiro de

1

Embora seja um tópico antigo, adicionar outra opção pode não prejudicar. (As funções lambda do JDK 1.8 parecem tornar isso mais fácil);

O problema pode ser dividido nas seguintes etapas;

  • Obtenha um valor mínimo para a lista de inteiros fornecida (para a qual gerar números aleatórios únicos)
  • Obtenha um valor máximo para a lista de inteiros fornecida
  • Use a classe ThreadLocalRandom (do JDK 1.8) para gerar valores inteiros aleatórios em relação aos valores inteiros mínimo e máximo encontrados anteriormente e, em seguida, filtre para garantir que os valores estejam de fato contidos na lista fornecida originalmente. Por fim, aplique distinto ao intstream para garantir que os números gerados sejam únicos.

Aqui está a função com alguma descrição:

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

}

Assim, para obter 11 números aleatórios exclusivos para o objeto de lista 'allIntegers', chamaremos a função como;

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

A função declara novo arrayList 'generatedUniqueIds' e preenche com cada inteiro aleatório exclusivo até o número necessário antes de retornar.

A classe PS ThreadLocalRandom evita valor de semente comum no caso de threads simultâneos.


0

tente isso

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

Isso não é significativamente diferente de outras respostas, mas eu queria a matriz de inteiros no final:

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

0

você pode usar a matriz booleana para preencher o verdadeiro se o valor for tomado, então o conjunto navegar pela matriz booleana para obter o valor conforme dado abaixo

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

Escolha n números aleatórios únicos de 0 a 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;
}

Imagine uma lista contendo números de 0 a m-1. Para escolher o primeiro número, basta usar rand.nextInt(m). Em seguida, remova o número da lista. Agora, restam m-1 números, então ligamos rand.nextInt(m-1). O número que obtemos representa a posição na lista. Se for menor que o primeiro número, então é o segundo número, já que a parte da lista anterior ao primeiro número não foi alterada pela remoção do primeiro número. Se a posição for maior ou igual ao primeiro número, o segundo número é posição + 1. Faça alguma derivação adicional, você pode obter este algoritmo.

Explicação

Este algoritmo tem complexidade O (n ^ 2). Portanto, é bom para gerar pequenas quantidades de números exclusivos a partir de um grande conjunto. Enquanto o algoritmo baseado em shuffle precisa de pelo menos O (m) para fazer o shuffle.

Além disso, o algoritmo baseado em shuffle precisa de memória para armazenar todos os resultados possíveis para fazer o shuffle, esse algoritmo não precisa.


0

Você pode usar a classe Coleções.

Uma classe de utilitário chamada Coleções oferece diferentes ações que podem ser executadas em uma coleção como um ArrayList (por exemplo, pesquisar os elementos, encontrar o elemento máximo ou mínimo, inverter a ordem dos elementos e assim por diante). Uma das ações que ele pode realizar é embaralhar os elementos. O shuffle moverá aleatoriamente cada elemento para uma posição diferente na lista. Ele faz isso usando um objeto Random. Isso significa que é aleatoriedade determinística, mas funcionará na maioria das situações.

Para embaralhar a ArrayList, adicione a importação de Coleções ao início do programa e use o método estático Shuffle. Leva o ArrayList para ser embaralhado como um parâmetro:

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

Você pode gerar n número aleatório único entre 0 e n-1 em 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

Este é o método mais simples para gerar valores aleatórios exclusivos em um intervalo ou a partir de uma matriz .

Neste exemplo, estarei usando uma matriz predefinida, mas você pode adaptar esse método para gerar números aleatórios também. Primeiro, criaremos um array de amostra para recuperar nossos dados.

  1. Gere um número aleatório e adicione-o à nova matriz.
  2. Gere outro número aleatório e verifique se ele já está armazenado no novo array.
  3. Se não, adicione e continue
  4. caso contrário, reitere a etapa.
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);

Agora, a partir do sampleList, iremos produzir cinco números aleatórios que são únicos.

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
}
        

Isso é conceitualmente muito simples. Se o valor aleatório gerado já existir, reiteraremos a etapa. Isso continuará até que todos os valores gerados sejam exclusivos.

Se você achou esta resposta útil, você pode votar nela, pois é muito simples no conceito em comparação com as outras respostas .


-2

Verifique isto

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
Jogando padrões java, legibilidade e usabilidade pela janela, hein?
Austin Wernli

O código não é uma resposta. Você escreve uma resposta e adiciona o código para explicar o que deseja.
Aditya

-2

Abaixo está uma forma que usei para gerar sempre um número único. A função aleatória gera um número e o armazena em um arquivo de texto e, na próxima vez que o verifica no arquivo, compara e gera um novo número exclusivo, portanto, desta forma, há sempre um novo número exclusivo.

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;

}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.