HomeAbout Me

흥미로운 알고리즘 - 루프 전략(Loop Strategy)

By kuper0201
Published in 알고리즘
2023-05-23
2 min read
흥미로운 알고리즘 - 루프 전략(Loop Strategy)

Table Of Contents

01
서론
02
문제
03
풀이
04
시뮬레이션
05
마무리

서론

Youtube에서 흥미로운 영상을 찾게 되었다.

Veritasium 채널의 확률 1000000000000000000000000000000배 높이기 영상이다.

필자는 해당 영상의 문제와 풀이를 보고 놀라움을 금치 못했고 영상의 풀이를 코드로 구현해 실제로 작동하는지 보고싶었다.

따라서 이번 글은 백준 문제 풀이에 대한 글은 아니지만 영상의 문제와 풀이를 정리하고 Java 코드로 구현해 보고자 한다.

먼저 영상에서 제시하는 문제는 다음과 같다.


문제

100명의 죄수들과 100개의 상자가 있는 방이 존재한다.

각 상자에는 죄수들의 번호가 적혀 있는 쪽지가 랜덤하게 들어있다.

100명의 죄수들은 한 명씩 방에 들어가 50개의 상자만을 열어 자신의 번호를 찾아야 한다.

100명의 죄수들이 모두 상자에서 자신의 번호를 찾으면 전원 석방되지만 단 한명이라도 자신의 번호를 찾지 못하면 전원 처형된다.

죄수들이 방에서 나갈 때에는 들어 왔을 때와 똑같은 상태로 만들어 두고 나가야 한다.(자신의 번호를 찾았을 경우에도 상자에 다시 넣어야 한다)

죄수들이 방에 들어오기 전에는 전략 회의를 할 수 있지만 상자를 열어본 후에는 어떤 소통도 불가하다.

또한 방이나 상자에 표시를 남기는 것도 불가능하다.

이 때 어떤 전략을 사용하면 죄수들이 석방 될 확률을 높일 수 있는가? (원래 문제는 확률이 얼마냐고 묻는 문제였지만 필자가 각색하였다.)


풀이

100개의 상자 중 50개를 열어 볼 수 있으므로 죄수 한 명이 자신의 번호를 찾을 확률은 12\frac{1}{2}이다.

한 명이 자신의 번호를 찾을 확률이 12\frac{1}{2}이므로 100명이 모두 자신의 번호를 찾을 확률은 12100\frac{1}{2^{100}}이다.

복권의 1등 당첨 확률이 18,145,060\frac{1}{8,145,060}이라고 한다.

21002^{100}은 8,145,060보다는 비교가 불가할 정도로 크기 때문에 죄수들이 석방 될 확률은 복권 1등에 당첨 될 확률보다 훨씬 낮다는 것을 알 수 있다.

하지만 죄수들이 Loop Strategy(루프 전략)을 사용하면 석방 확률을 약 31%까지 올릴 수 있다.

말이 안되는 것 같은 이 전략이 어떻게 작동하는지 확인해 보겠다.

  1. 죄수는 방에 들어가 자신의 번호 번째에 있는 상자를 열어 본다.
  2. 확인한 쪽지가 자신의 번호가 아니면 쪽지에 적힌 번호의 상자를 열어 본다.
  3. 자신의 번호를 찾을 때 까지 반복한다.

뭔가 거창한 방법일 거라 생각했지만 의외로 간단한 전략이다.

상자에 있는 쪽지의 번호를 따라가면 자신의 번호가 나온다는 사실은 직관적이지 않아 루프 전략이 성립하는지 알기 어렵다.

이에 대해 설명하자면 하나의 상자 안에는 하나의 쪽지가 들어 있고 각 쪽지는 다음 상자를 가리키는데 사용되므로 Loop가 성립한다.

쉽게 말하면 100개의 상자와 100개의 쪽지들이 각각 1대 1 대응되므로 Loop가 생성된다는 말이다.

Loop가 성립하면 한 바퀴를 돌아 제자리로 돌아 올 수 있다는 것을 의미하므로 자신의 번호와 같은 상자를 가리키는 쪽지가 존재한다는 것을 반증한다.

따라서 처음에 자신의 번호와 같은 번호의 상자를 고르고 루프를 계속 따라가다 보면 결국에는 자신의 번호를 찾을 수 있다.

문제는 루프의 크기가 50보다 클 경우이다.

시작점이 자신의 번호이므로 항상 루프의 마지막에서 자신의 번호를 찾을 수 있기 때문에 루프의 크기가 50보다 큰 경우에는 전체 루프를 확인하지 못하고 끝나버릴 것이다.

루프가 생성되는 전체 경우의 수 중 50보다 큰 루프의 크기를 가지지 않을 확률이 약 31%라고 한다. (자세한 계산은 영상에 잘 설명되어 있다)

그렇기 때문에 죄수들이 살아남을 수 있는 확률은 약 31%가 된다.


시뮬레이션

루프 전략이 무엇이고 어떻게 작동하는지 알았으므로 코드를 통해 실제로 작동하는지 확인해 보겠다.

루프 전략과 비교하기 위한 랜덤 전략이라고 부르는 하나의 전략을 더 추가했다.

랜덤 전략은 100개의 상자 중 중복을 제외하고 50개의 상자를 무작위로 열어보는 전략으로 처음에 이야기 했던 12100\frac{1}{2^{100}}의 확률을 갖는다.

이 전략에서 한번이라도 성공한다면 복권 1등에 당첨된 것보다 희박한 확률을 뚫어냈다는 것이다.

실제 작성 코드는 아래와 같고 주석을 통해 각 함수와 로직을 설명해 두었다.

코드 보기(Java)
import java.util.*;

public class Main {
    static final int PRISONER = 100; // 죄수의 인원
    static final int LIMIT = PRISONER / 2; // 상자를 열어 볼 수 있는 제한
    static final int TRIALS = 100000; // 시행 횟수
    static ArrayList<Integer> box = new ArrayList<>();

    public static void main(String[] args) {
        // 각 전략을 실행함
        doStrategy(false); // false - 랜덤 전략
        doStrategy(true); // true - 루프 전략
    }

    // 전략 실행 및 성공 비율 계산 함수
    static void doStrategy(boolean flag) {
        int success = 0; // 모든 죄수가 찾은 경우 저장
        boolean isFind; // 한 명의 죄수가 번호를 찾았는지 저장

        // 시행 횟수만큼 반복
        newtrial : for(int t = 0; t < TRIALS; t++) {
            initBox();

            // i번째 죄수가 상자를 열어 봄
            for (int i = 0; i < PRISONER; i++) {
                // flag : true - 루프전략 / false - 랜덤전략
                if(flag) isFind = loopStrategy(i);
                else isFind = randomStrategy(i);

                // 자기 번호 못찾았을 경우
                if (!isFind) continue newtrial;
            }

            // 모든 죄수가 찾았으므로 성공 횟수 증가
            success++;
        }

        if(flag) System.out.println(String.format("루프 전략 (성공 / 시행 횟수) : %d / %d", success, TRIALS));
        else System.out.println(String.format("랜덤 전략 (성공 / 시행 횟수) : %d / %d", success, TRIALS));
    }

    // 랜덤 전략 함수
    static boolean randomStrategy(int number) {
        // 이미 방문한 상자 표시용
        HashSet<Integer> visit = new HashSet<>();

        // 제한 횟수만큼 상자를 열어봄
        for(int i = 0; i < LIMIT; i++) {
            int boxNum = -1; // 열어볼 상자 번호

            // 이미 열어 본 상자라면 다른 상자 선택
            do {
                boxNum = (int)(Math.random() * PRISONER);
            } while(visit.contains(boxNum));
            visit.add(boxNum); // 열어본 상자 목록에 추가

            // 자기 번호 찾았을 경우 true
            if(number == box.get(boxNum)) return true;
        }

        // 제한 횟수만큼 열어봤는데 못찾은 경우 false
        return false;
    }

    // 루프 전략 함수
    static boolean loopStrategy(int number) {
        // 다음에 열어 볼 상자
        int next = number;

        // 제한 횟수만큼 상자 열어봄
        for(int i = 0; i < LIMIT; i++) {
            if(number == box.get(next)) return true; // 자기 번호 찾은 경우 true
            next = box.get(next); // 상자에 있던 번호가 다음에 열어볼 상자가 됨
        }

        // 제한 횟수만큼 열어봤는데 못찾은 경우 false
        return false;
    }

    // 상자 초기화 함수
    static void initBox() {
        box.clear(); // 상자 비우기
        for(int i = 0; i < PRISONER; i++) box.add(i); // 상자에 번호 넣기
        Collections.shuffle(box); // 상자 무작위로 섞기
    }
}

마무리

랜덤 전략 (성공 / 시행 횟수) : 0 / 100000
루프 전략 (성공 / 시행 횟수) : 31246 / 100000

랜덤 전략 (성공 / 시행 횟수) : 0 / 100000
루프 전략 (성공 / 시행 횟수) : 31112 / 100000

랜덤 전략 (성공 / 시행 횟수) : 0 / 100000
루프 전략 (성공 / 시행 횟수) : 31298 / 100000

작성한 시뮬레이션을 실행해 보니 위와 같은 결과가 나왔다.

예상한 대로 랜덤 전략을 이용한 시도는 한번도 성공하지 못했고 루프 전략은 꾸준하게 약 31%의 성공을 보여준다.


Tags

#algorithm
Previous Article
BOJ 3495 아스키 도형
kuper0201

kuper0201

안녕하세요!

Related Posts

BOJ 1009 분산처리
BOJ 1009 분산처리
2023-05-25
1 min

Quick Links

HomeAbout Me

Social Media