2010-01-25

A SAMPLE PROBLEM

Jon Bentley가 쓴 Programming Pearls 12장. SAMPLE PROBLEM을 정리한 글이다.

문제


여론 조사 기관에서는 N 명의 사람 중에서 M 명의 사람을 무작위로 추출하여 설문 조사를 한다.

M < N


N 명의 사람엔 0부터 시작하는 일련번호가 부여되어 있다. N을 5라고 가정하면 다음과 같다.

0 강백호
1 서태웅
2 채치수
3 정대만
4 송태섭

M이 2라면 이 중 동일한 확률로 2명을 추출해야 한다.

이는 0에서 N 사이에서 M 개의 숫자를 동일한 확률로 무작위로 추출하는 것에 다름아니다.

* 이 문제를 해결하는데 다음 2 함수를 사용할 수 있다.

bigrand : m 혹은 n 보다도 클 수 있는 정수를 무작위로 반환한다.
randint(i, j) : i에서 j 사이에 포함되어 있는 정수를 무작위로 반환한다.


솔루션 1

select = m
remaining = n
for i = [0, n)
  if (bigrand() % remaining) < select
    print i
    select--
  remaining--

* 추출된 숫자는 m 보다 많을 수도 m 보다 적을 수도 없다. 강조한 부분을 주의 깊게 보면 이를 파악할 수 있다.

일련번호가 0인 강백호가 선택될 확률은 2 / 5이지만 이 후에 있는 사람들이 선택될 확률은 앞 결과에 영향을 받는다. 예를 들어 강백호가 선택되면 일련번호가 1인 서태웅이 선택될 확률은 1 / 4이고, 강백호가 선택되지 않으면 서태웅이 선택될 확률은 2 / 4이다.

그러나 전체적으로는 서태웅이 추출될 확률도 2 / 5이다.

서태웅이 추출될 확률
= 강백호가 추출될 확률 (2 / 5) * 1 / 4 + 강백호가 추출되지 않을 확률 (3 / 5) * 2 / 4
= 2 / 20 + 6 / 20
= 2 / 5

이를 C++로 구현하면 다음과 같다.

void genknuth(int m, int n)
{
  for (int i = 0; i < n; i++)
    if ((bigrand() % (n - i) < m) {
      cout << i << "\n";
      m--:
    }
}


솔루션 2

set에 난수를 담는 방법도 있다.

* set에는 동일한 값이 들어 갈 수 없다.

initialize set S to empty
size = 0
while size < m do
  t = bigrand() % n
  if t is not in S
    insert t into S
    size++
print the elements of  S in sorted order

C++ STL을 이용하여 구현하면 다음과 같다.

void gensets(int m, int n)
{
  set<int> S;
  while (S.size() < m)
    S.insert(bigrand() % n);
  set<int>::iterator i;
  for (i < S.begin(); i != S.end(); ++i)
    cout << *i << "\n";
}


솔루션 3

n 개 크기 배열을 만든 후에 이들의 위치를 무작위로 변경(swap)하고 처음 m 개를 선택하는 방법도 있다. 화투나 포카에서 카드를 섞는 것(shuffle)과 유사하다.

for i = [0, n)
  swap(i, randint(i, n - 1))


C++를 구현하면 다음과 같다.

void genshuf(int m, int n)
{
  int i, j;
  int *x = new int[n];
  for (i = 0;; i < n; i++)
    x[i] = i;
  for (i = 0; i < m; i++) {
    j = randint(i, n - 1);
    int t = x[i]; x[i] = x[j]; x[j] = t;
  }
  sort(x, x + m)
  for (i = 0; i < m; i++)
    cout << x[i] << "\n";
}

개념 코드와는 다르게 m개 까지만 swap한다. (Ashley Shepherd와 Alex Woronow)



기타

만약 n이 100만이고 m이 n - 10이라면 조금은 다르게 접근해야 한다. 이 경우에는 추출되지 않을 10개를 계산하는 것이 적합하다.



프로그래밍 혹은 문제 해결은 다음과 같이 진행된다.

  1. Understand the Perceived Problem
  2. Specify an Abstract Problem
  3. Explore the Design Space
  4. Implement One Solution
  5. Retrospect

2010-01-13

Putty에서 캐릭터 셋 고정

Putty에서 캐릭터 셋을 UTF-8로 변경해도 다시 접속하면 값이 초기화되는 문제가 있었다.

이를 해결하는 방법이다.

http://k.daum.net/qna/view.html?qid=3yKL8



결론은 계정 정보 자체를 저장해야 한다는 것이다... 직관적이지 못하다...

2010-01-12

Color...

00FF66과 같은 HEX 문자열로 java.awt.Color 객체를 만들어야 했다.

Color 클래스 생성자는 RGB 값만을 파라미터로 받으니 귀찮은 데이터 전환을 해야 하나 고민하다가... 다음 메소드를 이용하면 된다는 걸 알았다.

Color color = Color.decode("00FF66");

그런데 예외가 발생했다. 앞에 #을 붙여 주어야 했다.

Color color = Color.decode("#00FF66");


그런데 스택트레이스 덕분에

Exception in thread "main" java.lang.NumberFormatException: For input string: "0ff66"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
    at java.lang.Integer.parseInt(Integer.java:458)
    at java.lang.Integer.valueOf(Integer.java:528)
    at java.lang.Integer.decode(Integer.java:958)
    at java.awt.Color.decode(Color.java:707)

실제로는 Integer 클래스의 decode 메소드를 사용하면 된다는 것도 알았다.


2010-01-07

이클립스에 static import 처리

이클립스에서 static import를 편리하게 사용하려면 다음 설정을 해준다.

1. Organize Import 처리

http://www.prever.co.kr/josh/?p=35

2. Code Assist 처리

http://blog.naver.com/kcufl/60094963610