자바에서 고유 한 난수 생성


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 2011



이것은 유용 할 수 있습니다. 고유 한 임의 ID 생성기
Erfan Ahmed 2011

답변:


144
  • 목록 구조 에서 범위의 각 숫자를 순차적으로 추가합니다 .
  • 셔플 하십시오.
  • 첫 번째 'n'을 가져옵니다.

다음은 간단한 구현입니다. 이것은 1-10 범위에서 3 개의 고유 한 난수를 인쇄합니다.

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

Mark Byers가 현재 삭제 된 답변에서 지적했듯이 원래 접근 방식을 사용한 수정의 첫 번째 부분은 단일 Random인스턴스 만 사용하는 것 입니다.

그것이 숫자가 동일하게 만드는 원인입니다. Random예는 밀리 세컨드의 현재 시각에 의해 시드된다. 특정 시드 값의 경우 'random'인스턴스는 정확히 동일한 의사 난수 시퀀스를 반환합니다 .

참고 것을 public Integer​(int value)생성자는 deprecated자바 (9) 때문이다.

첫 번째 for 루프는 간단히 다음과 같이 변경할 수 있습니다.

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

3
하나의 임의 인스턴스를 지적 +1 하고 질문에 대답. :)
Mark Byers 2011

전체 범위를 섞을 필요는 없습니다. n 개의 고유 한 숫자를 원하면 Fisher-Yates 셔플을 사용하여 처음 n 개의 위치 만 셔플하면됩니다. 이것은 큰 목록과 작은 n에 도움이 될 수 있습니다.
rossum

59

자바로 8 + 당신이 사용할 수있는 ints방법을 Random얻기 위해 IntStream다음 임의 값을 distinct하고 limit고유 한 임의의 값의 번호로 스트림을 줄일 수 있습니다.

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

Random또한 필요한 경우 LongStreams 및 DoubleStreams 를 만드는 메서드도 있습니다 .

임의의 순서로 범위의 모든 숫자 (또는 많은 양)를 원하는 경우 모든 숫자를 목록에 추가하고 섞은 다음 위의 예제가 현재 구현되어 있으므로 처음 n을 취하는 것이 더 효율적일 수 있습니다. 요청 된 범위에서 난수를 생성하고 세트를 통해 전달함으로써 ( Rob Kielty의 답변 과 유사 ), 새로운 고유 번호를 생성 할 확률이 발견 될 때마다 감소하기 때문에 제한에 전달 된 양보다 더 많이 생성해야 할 수 있습니다. 다른 방법의 예는 다음과 같습니다.

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 [] indices = new Integer [n]; Arrays.setAll (인덱스, i-> i); Collections.shuffle (Arrays.asList (indices)); return Arrays.stream (indices) .mapToInt (Integer :: intValue) .toArray (); `
AbuNassar

18
  1. 100 개의 숫자로 구성된 배열을 만든 다음 순서를 무작위로 지정합니다.
  2. 범위가 100 인 의사 난수 생성기를 고안하십시오.
  3. 100 개의 요소로 구성된 부울 배열을 만든 다음 해당 숫자를 선택할 때 요소를 true로 설정합니다. 다음 번호를 선택할 때 배열을 확인하고 배열 요소가 설정되어 있는지 다시 시도하십시오. ( long개별 비트에 액세스하기 위해 이동 및 마스크하는 위치 의 배열을 사용하여 쉽게 명확하게 부울 배열을 만들 수 있습니다 .)

2
대체 접근 방식의 경우 +1; pick()예입니다.
trashgod

1
부울 배열을 사용하는 대신 HashSet이미 생성 한 숫자를 저장하고 이미 해당 숫자를 생성 contains했는지 테스트 하는 데 사용 하는를 사용할 수 있습니다. 은 HashSet아마 부울 배열보다 약간 느릴 수 있지만, 메모리를 적게 차지합니다.
Rory O'Kane 2013

1
@ RoryO'Kane-long [2]의 배열로 구현하면 부울 배열이 더 적은 공간을 차지할 것이라고 확신합니다. 도대체 HashSet을 그렇게 작게 만들 수는 없습니다.
Hot Licks 2013

마지막 접근 방식은 전체 시퀀스를 생성하기 위해 잘 정의 된 단계 수가 없기 때문에 약간 추합니다. 또한 휠을 재발 명 할 필요가 없습니다 -BitSet .
파벨 Horal

16

Collections.shuffle()100 개의 숫자를 모두 사용 하고 여기에 표시된대로 처음 5 개를 선택합니다 .


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

나는 Anand의 대답을 리팩터링하여 Set의 고유 속성을 사용할뿐만 아니라 세트에 set.add()대한 추가가 실패 할 때 반환되는 부울 false도 사용했습니다 .

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는 원시 루트 mod 101입니다. 즉, 2 mod 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

이 질문의 (중복되었습니다 또 다른 질문에서 이리가 자바에서 생성 고유 한 임의의 숫자 )

  1. 배열에 1 ~ 100 개의 숫자를 저장합니다.

  2. 1에서 100 사이의 난수를 위치로 생성하고 array [position-1]을 반환하여 값을 가져옵니다.

  3. 배열에서 숫자를 사용하면 값을 -1로 표시합니다 (이 숫자가 이미 사용되었는지 확인하기 위해 다른 배열을 유지할 필요가 없음).

  4. 배열의 값이 -1이면 임의의 숫자를 다시 가져와 배열의 새 위치를 가져옵니다.


1

나는이 문제에 대한 쉬운 해결책을 가지고있다. 이것으로 우리는 n 개의 고유 한 난수를 쉽게 생성 할 수있다. 그 논리는 누구나 어떤 언어로도 사용할 수있다.

for(int i=0;i<4;i++)
        {
            rn[i]= GenerateRandomNumber();
            for (int j=0;j<i;j++)
            {
                if (rn[i] == rn[j])
                {
                    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

값을 취하면 부울 배열을 사용하여 true를 채울 수 있습니다.

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

0부터 m-1까지 n 개의 고유 한 난수를 선택합니다.

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를 섞으려면 Collections 가져 오기를 프로그램 맨 위에 추가 한 다음 Shuffle 정적 메서드를 사용합니다. 배열 목록을 매개 변수로 셔플합니다.

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에서)를 사용하여 이전에 찾은 최소 및 최대 정수 값에 대해 임의의 정수 값을 생성 한 다음 해당 값이 원래 제공된 목록에 실제로 포함되도록 필터링합니다. 마지막으로 생성 된 숫자가 고유한지 확인하기 위해 인트 스트림에 구분을 적용합니다.

다음은 몇 가지 설명이있는 함수입니다.

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

}

따라서 'allIntegers'목록 객체에 대해 11 개의 고유 한 난수를 얻기 위해 다음과 같은 함수를 호출합니다.

    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고유 한 5 개의 난수를 생성합니다.

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

Java에서 0에서 n-1 사이의 n 고유 난수를 생성 할 수 있습니다.

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
자바 표준, 가독성 및 유용성을 창 밖으로 던지고 있습니까?
austin wernli

코드는 답이 아닙니다. 답을 작성하고 원하는 것을 설명하는 코드를 추가합니다.
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;

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.