[java] 중복이없는 난수 만들기

이 경우 MAX가 5에 불과하므로 중복을 하나씩 확인할 수 있지만 어떻게하면 더 간단하게 할 수 있을까요? 예를 들어 MAX의 값이 20이면 어떻게됩니까? 감사.

int MAX = 5;

for (i = 1 , i <= MAX; i++)
{
        drawNum[1] = (int)(Math.random()*MAX)+1;

        while (drawNum[2] == drawNum[1])
        {
             drawNum[2] = (int)(Math.random()*MAX)+1;
        }
        while ((drawNum[3] == drawNum[1]) || (drawNum[3] == drawNum[2]) )
        {
             drawNum[3] = (int)(Math.random()*MAX)+1;
        }
        while ((drawNum[4] == drawNum[1]) || (drawNum[4] == drawNum[2]) || (drawNum[4] == drawNum[3]) )
        {
             drawNum[4] = (int)(Math.random()*MAX)+1;
        }
        while ((drawNum[5] == drawNum[1]) ||
               (drawNum[5] == drawNum[2]) ||
               (drawNum[5] == drawNum[3]) ||
               (drawNum[5] == drawNum[4]) )
        {
             drawNum[5] = (int)(Math.random()*MAX)+1;
        }

}



답변

가장 간단한 방법은 가능한 숫자 목록 (1..20 또는 기타)을 만든 다음 Collections.shuffle . 그런 다음 원하는 많은 요소를 선택하십시오. 범위가 마지막에 필요한 요소의 수와 같을 때 유용합니다 (예 : 카드 더미를 섞는 경우).

1..10,000 범위에서 임의의 요소 10 개를 원하면 그렇게 잘 작동하지 않습니다. 불필요하게 많은 작업을 수행하게됩니다. 이 시점에서 지금까지 생성 한 값 세트를 유지하고 다음 값이 아직 없을 때까지 루프에서 숫자를 계속 생성하는 것이 좋습니다.

if (max < numbersNeeded)
{
    throw new IllegalArgumentException("Can't ask for more numbers than are available");
}
Random rng = new Random(); // Ideally just create one instance globally
// Note: use LinkedHashSet to maintain insertion order
Set<Integer> generated = new LinkedHashSet<Integer>();
while (generated.size() < numbersNeeded)
{
    Integer next = rng.nextInt(max) + 1;
    // As we're adding to a set, this will automatically do a containment check
    generated.add(next);
}

하지만 세트 선택에 LinkedHashSet주의하세요. 여기에서 신경 쓰는 삽입 순서를 유지하므로 매우 고의적으로 사용 했습니다.

또 다른 옵션은 매번 범위를 줄이고 기존 값을 보완 하여 항상 진행하는 것입니다. 예를 들어 0..9 범위에서 3 개의 값을 원한다고 가정합니다. 첫 번째 반복에서 0..9 범위의 숫자를 생성합니다. 4를 생성한다고 가정 해 보겠습니다.

두 번째 반복에서 0..8 범위의 숫자를 생성합니다. 생성 된 숫자가 4보다 작 으면 그대로 유지합니다. 그렇지 않으면 하나를 추가합니다. 4가없는 0..9의 결과 범위를 얻습니다. 그런 식으로 7을 얻는다고 가정합니다.

세 번째 반복에서 0..7 범위의 숫자를 생성합니다. 생성 된 숫자가 4보다 작 으면 그대로 유지합니다. 4 또는 5 인 경우 하나를 추가합니다. 6 또는 7이면 2 개를 더합니다. 이렇게하면 결과 범위는 4 또는 6이없는 0..9입니다.


답변

방법은 다음과 같습니다.

import java.util.ArrayList;
import java.util.Random;

public class Test {
    public static void main(String[] args) {
        int size = 20;

        ArrayList<Integer> list = new ArrayList<Integer>(size);
        for(int i = 1; i <= size; i++) {
            list.add(i);
        }

        Random rand = new Random();
        while(list.size() > 0) {
            int index = rand.nextInt(list.size());
            System.out.println("Selected: "+list.remove(index));
        }
    }
}

존경 씨 스키트는 지적 아웃이 있기 때문에 :
만약을 n 개의 무작위로 선택된 번호의 수를 당신이 선택하고자하는 N 선택할 수있는 번호의 전체 표본 공간입니다 :

  1. 만약 N << N , 당신은 당신이 선택했음을 번호를 저장하고 선택한 번호가 거기에 있는지 목록을 확인해야합니다.
  2. 경우 N ~ = N은 , 당신은 아마 전체 샘플 공간을 포함하는 목록을 채우고 다음을 선택로에서 숫자를 제거하여 내 방법을 사용해야합니다.


답변

//random numbers are 0,1,2,3 
ArrayList<Integer> numbers = new ArrayList<Integer>();
Random randomGenerator = new Random();
while (numbers.size() < 4) {

    int random = randomGenerator .nextInt(4);
    if (!numbers.contains(random)) {
        numbers.add(random);
    }
}


답변

LFSR을 사용하여 “무작위”주문 번호를 수행하는 또 다른 방법이 있습니다. 다음을 살펴보십시오.

http://en.wikipedia.org/wiki/Linear_feedback_shift_register

이 기술을 사용하면 인덱스별로 정렬 된 난수를 얻고 값이 중복되지 않도록 할 수 있습니다.

그러나 랜덤 생성이 결정적이기 때문에 이들은 TRUE 난수가 아닙니다.

그러나 경우에 따라이 기술을 사용하여 셔플 링을 사용할 때 난수 생성 처리량을 줄일 수 있습니다.

여기 자바의 LFSR 알고리즘 (기억하지 않는 곳에서 가져 왔습니다) :

public final class LFSR {
    private static final int M = 15;

    // hard-coded for 15-bits
    private static final int[] TAPS = {14, 15};

    private final boolean[] bits = new boolean[M + 1];

    public LFSR() {
        this((int)System.currentTimeMillis());
    }

    public LFSR(int seed) {
        for(int i = 0; i < M; i++) {
            bits[i] = (((1 << i) & seed) >>> i) == 1;
        }
    }

    /* generate a random int uniformly on the interval [-2^31 + 1, 2^31 - 1] */
    public short nextShort() {
        //printBits();

        // calculate the integer value from the registers
        short next = 0;
        for(int i = 0; i < M; i++) {
            next |= (bits[i] ? 1 : 0) << i;
        }

        // allow for zero without allowing for -2^31
        if (next < 0) next++;

        // calculate the last register from all the preceding
        bits[M] = false;
        for(int i = 0; i < TAPS.length; i++) {
            bits[M] ^= bits[M - TAPS[i]];
        }

        // shift all the registers
        for(int i = 0; i < M; i++) {
            bits[i] = bits[i + 1];
        }

        return next;
    }

    /** returns random double uniformly over [0, 1) */
    public double nextDouble() {
        return ((nextShort() / (Integer.MAX_VALUE + 1.0)) + 1.0) / 2.0;
    }

    /** returns random boolean */
    public boolean nextBoolean() {
        return nextShort() >= 0;
    }

    public void printBits() {
        System.out.print(bits[M] ? 1 : 0);
        System.out.print(" -> ");
        for(int i = M - 1; i >= 0; i--) {
            System.out.print(bits[i] ? 1 : 0);
        }
        System.out.println();
    }


    public static void main(String[] args) {
        LFSR rng = new LFSR();
        Vector<Short> vec = new Vector<Short>();
        for(int i = 0; i <= 32766; i++) {
            short next = rng.nextShort();
            // just testing/asserting to make 
            // sure the number doesn't repeat on a given list
            if (vec.contains(next))
                throw new RuntimeException("Index repeat: " + i);
            vec.add(next);
            System.out.println(next);
        }
    }
}


답변

당신은 당신이 원하는 얼마나 많은 숫자를 지정할 수 있습니다 또 다른 방법 sizeminmax반환 된 숫자의 값

public static int getRandomInt(int min, int max) {
    Random random = new Random();

    return random.nextInt((max - min) + 1) + min;
}

public static ArrayList<Integer> getRandomNonRepeatingIntegers(int size, int min,
        int max) {
    ArrayList<Integer> numbers = new ArrayList<Integer>();

    while (numbers.size() < size) {
        int random = getRandomInt(min, max);

        if (!numbers.contains(random)) {
            numbers.add(random);
        }
    }

    return numbers;
}

사용하려면 0에서 25 사이의 7 개의 숫자를 반환합니다.

    ArrayList<Integer> list = getRandomNonRepeatingIntegers(7, 0, 25);
    for (int i = 0; i < list.size(); i++) {
        System.out.println("" + list.get(i));
    }


답변

이것은 다음에서 훨씬 더 간단합니다 java-8.

Stream.generate(new Random()::ints)
            .distinct()
            .limit(16) // whatever limit you might need
            .toArray(Integer[]::new);


답변

반복되지 않는 난수를 갖는 가장 효율적이고 기본적인 방법은이 의사 코드로 설명됩니다. 중첩 된 루프 또는 해시 된 조회가 필요하지 않습니다.

// get 5 unique random numbers, possible values 0 - 19
// (assume desired number of selections < number of choices)

const int POOL_SIZE = 20;
const int VAL_COUNT = 5;

declare Array mapping[POOL_SIZE];
declare Array results[VAL_COUNT];

declare i int;
declare r int;
declare max_rand int;

// create mapping array
for (i=0; i<POOL_SIZE; i++) {
   mapping[i] = i;
}

max_rand = POOL_SIZE-1;  // start loop searching for maximum value (19)

for (i=0; i<VAL_COUNT; i++) {
    r = Random(0, max_rand); // get random number
    results[i] = mapping[r]; // grab number from map array
    mapping[r] = max_rand;  // place item past range at selected location

    max_rand = max_rand - 1;  // reduce random scope by 1
}

첫 번째 반복이 시작하기 위해 난수 3을 생성했다고 가정합니다 (0-19). 그러면 결과 [0] = 매핑 [3], 즉 값 3이됩니다. 그런 다음 매핑 [3]을 19에 할당합니다.

다음 반복에서 난수는 5 (0-18)였습니다. 그러면 결과 [1] = mapping [5], 즉 값 5가됩니다. 그런 다음 mapping [5]를 18에 할당합니다.

이제 다음 반복이 다시 3을 선택했다고 가정합니다 (0-17). results [2]에는 mapping [3]의 값이 할당되지만 이제이 값은 3이 아니라 19입니다.

이 동일한 보호 기능은 동일한 숫자를 연속으로 5 번 얻더라도 모든 숫자에 대해 유지됩니다. 예를 들어, 난수 생성기가 0을 연속으로 5 번 주면 결과는 [0, 19, 18, 17, 16]이됩니다.

같은 숫자를 두 번 얻지 못할 것입니다.