프로그래밍/AI로 통계공부

[Python] 로또 분석 서비스를 만들어봤다 #5 — 로또 공은 과거를 기억하지 않는다

Tiboong 2026. 3. 22. 16:01
728x90

⚠️ 이 글은 통계 분석 교육 목적이며, 로또 당첨을 보장하지 않습니다. 모든 조합의 당첨 확률은 동일하게 1/8,145,060입니다.

 


시리즈 링크


"적게 나온 번호가 따라잡아야 하지 않나?"

#4에서 "나올 때 됐다"가 도박사의 오류라는 걸 증명했다.

그러면 이런 질문이 나온다.

"아니, 큰 수의 법칙에 의하면 횟수가 많아지면 각 번호의 출현 비율이 같아져야 하잖아. 그러면 적게 나온 번호가 결국 따라잡아야 하는 거 아냐?"

 

이 질문은 진짜 좋은 질문이다. 그리고 대부분의 사람이 큰 수의 법칙을 잘못 이해하고 있다.


큰 수의 법칙이 진짜로 말하는 것

큰 수의 법칙(Law of Large Numbers)의 정확한 의미:

시행 횟수가 충분히 많아지면, 관측된 비율이 이론적 확률에 수렴한다.

 

핵심은 "비율"이다. "횟수"가 아니다.

이게 무슨 차이인지 실제 데이터로 보자.


실제 데이터: 9번 vs 20번

1회~1,207회 전체 데이터에서, 가장 적게 나온 번호와 가장 많이 나온 번호를 추적했다.

500회 시점:

  • 9번 (최소): 54회 출현 (10.8%)
  • 20번 (최대): 83회 출현 (16.6%)
  • 횟수 차이: 29회
  • 비율 차이: 5.8%p

1,207회 시점:

  • 9번: 132회 출현 (10.9%)
  • 20번: 166회 출현 (13.8%)
  • 횟수 차이: 34회
  • 비율 차이: 2.8%p

눈치챘나?

횟수 차이: 29회 → 34회. 오히려 벌어졌다.

"적게 나온 번호가 따라잡았다"는 사실이 아니다. 700회가 더 지났는데 차이는 5회 더 벌어졌다.


그런데 비율은?

같은 데이터를 비율로 보면 완전히 다른 그림이 나온다.

500회 시점:

  • 9번: 10.8%
  • 20번: 16.6%
  • 비율 차이: 5.8%p

1,207회 시점:

  • 9번: 10.9%
  • 20번: 13.8%
  • 비율 차이: 2.8%p

비율 차이: 5.8%p → 2.8%p. 줄어들었다!

이게 큰 수의 법칙이 진짜로 말하는 것이다.


비유로 이해하기

마라톤으로 비유해보자.

A 선수와 B 선수가 있다. A는 시속 10km, B는 시속 12km로 달린다.

1시간 후:

  • A: 10km, B: 12km → 차이 2km

10시간 후:

  • A: 100km, B: 120km → 차이 20km

100시간 후:

  • A: 1,000km, B: 1,200km → 차이 200km

차이는 계속 벌어진다. 하지만 비율은?

  • 1시간 후: B/A = 1.20배 (20% 앞서)
  • 10시간 후: B/A = 1.20배 (여전히 20%)
  • 100시간 후: B/A = 1.20배 (변함없음)

로또에서도 마찬가지다. 각 번호의 진짜 확률은 동일(1/45)하지만, 한번 벌어진 횟수 차이는 줄어들지 않는다. 비율만 수렴할 뿐이다.


코드로 확인하기

from collections import Counter
import pandas as pd

df = pd.read_excel('lotto_data.xlsx')

# 번호별 누적 출현 추적
def track_cumulative(df, target_nums, checkpoints):
    all_nums = []
    results = {cp: {} for cp in checkpoints}
    
    for idx, row in df.iterrows():
        nums = [row[f'num{i}'] for i in range(1, 7)]
        all_nums.extend(nums)
        
        round_num = int(row['round'])
        if round_num in checkpoints:
            freq = Counter(all_nums)
            for n in target_nums:
                results[round_num][n] = freq.get(n, 0)
    
    return results

# 500회, 1207회 시점 비교
results = track_cumulative(df, [9, 20], [500, 1207])

for cp in [500, 1207]:
    n9 = results[cp][9]
    n20 = results[cp][20]
    print(f"\n{cp}회 시점:")
    print(f"  9번: {n9}회 ({n9/cp*100:.1f}%)")
    print(f"  20번: {n20}회 ({n20/cp*100:.1f}%)")
    print(f"  횟수 차이: {n20-n9}회")
    print(f"  비율 차이: {(n20-n9)/cp*100:.1f}%p")

 

출력:

500회 시점:
  9번: 54회 (10.8%)
  20번: 83회 (16.6%)
  횟수 차이: 29회
  비율 차이: 5.8%p

1207회 시점:
  9번: 132회 (10.9%)
  20번: 166회 (13.8%)
  횟수 차이: 34회
  비율 차이: 2.8%p

 


이게 "이제 쉰다" 전략에 주는 교훈

나는 이 서비스에 "연속 미출현" 전략을 넣었다. "오래 안 나온 번호를 우선 선택"하는 알고리즘이다.

논리는 이랬다:

  • 9번이 54회밖에 안 나왔으니, 곧 따라잡아야 한다
  • 따라서 9번을 넣으면 유리하다

결론: 틀렸다.

큰 수의 법칙은 9번이 "따라잡아야 한다"고 말하지 않는다. 비율이 수렴하면 그만이고, 비율이 수렴하는 방법은 두 가지다:

  1. 9번이 많이 나와서 따라잡는다 ← 사람들이 기대하는 것
  2. 그냥 둘 다 비슷한 비율로 나오면서, 차이는 유지되거나 벌어진다 ← 실제로 일어나는 것

실제 데이터가 증명한다. 700회가 더 지나도 9번은 20번을 따라잡지 못했다. 차이는 오히려 29회에서 34회로 벌어졌다.


결론

큰 수의 법칙은 "비율의 수렴"이지 "횟수의 따라잡기"가 아니다.

  • ❌ "적게 나온 번호가 곧 많이 나올 것이다"
  • ✅ "적게 나온 번호의 출현 비율이 천천히 평균에 가까워질 것이다"
  • ✅ "하지만 횟수 차이는 줄어들지 않거나, 오히려 벌어질 수 있다"

로또 공은 "지금까지 54번 나왔으니 좀 더 나와야지"라고 생각하지 않는다.

로또 공은 과거를 기억하지 않는다.

이건 #4의 도박사의 오류와 같은 뿌리다. 매 회차는 독립 시행이고, 이전 결과가 다음 결과에 영향을 주지 않는다. 큰 수의 법칙은 이 원칙을 위반하는 게 아니라, 오히려 확인해주는 것이다.


다음 편 예고

🔥 #6 HOT 번호의 비밀 — 27번이 8회 중 6회 나온 이유

큰 수의 법칙으로 보면 의미 없는데, 최근 8회 중 6회 출현한 27번은 뭘까? "핫 넘버"가 진짜 존재하는지 데이터로 파헤친다.

728x90