HashSet 이용 (2020년 4월 24일 추가)

Set 자료형은 중복을 허용하지 않는다는 특성을 이용한 코드입니다.

import java.util.HashSet;
import java.util.Set;

public class SetTest {

  public static void main(String[] args) {
    Set<Integer> lotto = new HashSet<>();

    while(lotto.size() < 6) {
      lotto.add((int)(Math.random() * 45 + 1));
    }

    System.out.println(lotto);

    // Optional: Set to array
    Integer[] lottoArr = new Integer[6];
    lotto.toArray(lottoArr);
    bubbleSort(lottoArr);

    for(int i : lottoArr) {
      System.out.print(i + "\t");
    }

  }

  private static void bubbleSort(Integer[] array) {
    // 버블정렬
    boolean isContinue = false;
    
    int tempNum;
    for (int i = 0; i < (array.length - 1); i++) {            
      for (int j = 0; j < (array.length - i - 1); j++) {
        if (array[j] > array[j + 1]) {
          tempNum = array[j];
          array[j] = array[j + 1];
          array[j + 1] = tempNum;
          isContinue = true;
        }
      }
      if (!isContinue) {
        break;
      }
    }
  }

}

이 방법이 제일 간단합니다. 밑의 내용은 보지마세요.


랜덤값을 뽑는 게 어려운 건 아니지만 중복없이 랜덤을 뽑는건 상당히 까다로운 작업입니다. 스트레이트로 접근하려고 하면 매우 지저분한 코드가 나올 수 있습니다. 프로그래밍 알고리즘 중에 카드뽑기(shuffle) 기법이라는 게 있는데요, 아래와 같은 순서로 진행됩니다.

  1. 배열 내에서 임의의 두 값을 선택한 뒤 스왑(swap)한다.
  2. 충분한 수 만큼 반복한다.
  3. 충분한 수 만큼 반복했다면 배열 내 원소들은 임의의 순서대로 섞여있을 것이며 여기서 원하는 갯수만큼 앞에서든 뒤에서든 추출한다.

카드뽑기 알고리즘을 사용하려면 먼저 스왑에 대해 알아볼 필요가 있습니다. 스왑(swap) 기법은 배열 등에서 앞과 뒤를 교환하는 알고리즘으로, 배열의 정렬 등에서 사용하며 정보처리기사에서도 단골로 출제되는 문제입니다. 아래는 스왑 예제입니다.

public class ArraySwap {
    
    public static void main(String[] args) {
        char[] A = new char[2];
        char temp;
        A[0] = 'A';
        A[1] = 'B';
        System.out.print(""+A[0]+A[1]);
        
        temp = A[0];
        A[0] = A[1];
        A[1] = temp;
        System.out.println(""+A[0]+A[1]+": Dancing Queen");
        System.out.println("\nYou can dance\r\n" + 
                "You can jive\r\n" + 
                "Having the time of your life\r\n" + 
                "Ooh, see that girl\r\n" + 
                "Watch that scene\r\n" + 
                "Dig in the dancing queen");
        
    }
 
}

위의 코드에 나오는 가수의 이름은?

 

카드뽑기를 할건데 주제가 로또이므로 숫자 6개를 추출합니다. 반복할 횟수는 시스템의 성능과 알고리즘 시간 등을 고려하여 정하는데 천단위 경우일 경우에는 생각처럼 잘 안섞여서 만단위 이상으로 충분히 진행하는 것이 좋겠습니다.

import blog.gui.PrintArray;

public class Lotto {
  
  static final int LOTTO_NUM = 45;
 
    public static void main(String[] args) {
        int[] lotto = new int[LOTTO_NUM];
        int x, y;
        
        int temp;
        
        // 1~45 일괄 채워넣기
        for (int i=0; i < lotto.length; i++)
        {
            lotto[i] =  i + 1;
            // System.out.println(lotto[i]);
        }
        
        // 섞기
        for(int i = 1; i <= 100000; i++)
        {
            x = (int)(Math.random() * LOTTO_NUM);
            y = (int)(Math.random() * LOTTO_NUM);
            
            temp = lotto[x];
            lotto[x] = lotto[y];
            lotto[y] = temp;
        }
        
        // 새 배열에 옮김
        int[] chosenLotto = new int[6];
        
        for(int i = 0; i < 6; i++ )
        {
            chosenLotto[i] = lotto[i];
        }
        
        // (추가) 버블정렬
        int tempNum;
        for (int i=0; i < (chosenLotto.length); i++)
        {            
            for (int j=0; j < (chosenLotto.length - 1 ); j++)
            {
                if (chosenLotto[j] > chosenLotto[j+1])
                {
                    tempNum = chosenLotto[j];
                    chosenLotto[j] = chosenLotto[j+1];
                    chosenLotto[j+1] = tempNum;
                }
            }
        
        }
        
        // 표시
        new PrintArray("로또 번호 뽑기", chosenLotto);

    }
}

new PrintArray()는 Java Swing 예제: 다차원 배열 표시하기 (기초 설정 방법, for문으로 swing 요소 반복 등) 에 있습니다.

 

자바 8 이상에서 사용할 수 있는 Collections를 사용한 고급 기법에 대해 알아보겠습니다. Collections.shuffle(List<?> list); 를 사용하면 카드뽑기를 훨씬 간편하게 할 수 있습니다.

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import blog.gui.PrintArray;

public class LottoShuffle {

  static final int LOTTO_NUM = 45;
  public static void main(String[] args) {
      int[] lotto = new int[LOTTO_NUM];
        
        // 1~45 일괄 채워넣기
        for (int i=0; i < lotto.length; i++)
        {
            lotto[i] =  i + 1;
        }
        
        List<Integer> list = Arrays.stream( lotto ).boxed().collect( Collectors.toList() );
        Collections.shuffle(list);	// 섞기
        List<Integer> selection = list.subList(0, 6);	// 6개 추출
        Collections.sort(selection); // 추출한 6개 정렬 
        // selection.forEach(System.out::println);
        
        new PrintArray("로또 번호 뽑기", selection);
  }
}

참고로 Collectionsshufflesort는 리턴형이 void이며, 배열이 아니라 리스트 등의 Collection자료형에서 사용 가능한 옵션이므로 배열을 리스트로 바꿔줘야 합니다. 20라인의 Arrays.stream이 그 역할을 합니다. int[] 형 배열은 스트림으로 변환하는 것이 Wrapper-object 관련 문제를 방지하는 데 도움이 됩니다.