⚠️ 이 글은 통계 분석 교육 목적이며, 로또 당첨을 보장하지 않습니다. 모든 조합의 당첨 확률은 동일하게 1/8,145,060입니다.
시리즈 링크
- #1 로또 번호를 만들어 볼까?
- #2 로또 번호를 맞춰 볼까?
- #3 로또 분석 서비스를 만들어봤다
- #4 "나올 때 됐다"를 검증해봤다 ← 지금 읽고 있는 글
이 전략 카드 기억하시죠?

🔄 연속 미출현 (Consecutive Absence) "오래 안 나온 번호를 우선합니다" 신뢰도: 70%
서비스를 만들 때 이 전략에 "신뢰도 70%"라고 써놨다. 오늘은 이게 진짜인지 까본다.
"나올 때 됐다"의 논리
로또를 사본 사람이라면 한 번쯤 이렇게 생각해봤을 거다.
"7번이 10회 연속 안 나왔으니까, 이번엔 나올 때 됐다."
직관적으로 그럴듯하다. 45개 중 하나가 10번이나 빠졌으면 슬슬 나와야 하지 않겠나?
이 전략의 알고리즘은 간단하다:
def get_consecutive_absence(df, current_round):
"""각 번호의 마지막 출현 이후 경과 회차를 계산"""
last_seen = {}
for _, row in df[df['round'] <= current_round].iterrows():
for col in ['num1','num2','num3','num4','num5','num6']:
last_seen[int(row[col])] = int(row['round'])
# 갭 = 현재 회차 - 마지막 출현 회차
gaps = {}
for n in range(1, 46):
if n in last_seen:
gaps[n] = current_round - last_seen[n]
else:
gaps[n] = current_round # 한 번도 안 나온 경우
return gaps
오래 안 나온 순으로 정렬해서, 상위 번호들로 조합을 만든다. 끝. 알고리즘이라고 하기에도 민망할 정도로 간단하다.
문제는 — 이게 맞냐는 거다.
먼저, 갭이 뭔지 보자
번호가 출현한 간격을 "갭"이라고 부른다. 5번을 예로 들어보겠다.
# 5번의 출현 회차 추출
appearances = []
for _, row in df.iterrows():
nums = [row['num1'], row['num2'], row['num3'], row['num4'], row['num5'], row['num6']]
if 5 in nums:
appearances.append(int(row['round']))
# 갭 계산
gaps = [appearances[i] - appearances[i-1] for i in range(1, len(appearances))]
print(f"5번 총 출현: {len(appearances)}회 / 1,213회")
print(f"평균 갭: {np.mean(gaps):.1f}회")
print(f"최소 갭: {min(gaps)}회 (바로 다음 회에 또 나옴)")
print(f"최대 갭: {max(gaps)}회")
5번 총 출현: 153회 / 1,213회
평균 갭: 7.8회
최소 갭: 1회 (바로 다음 회에 또 나옴)
최대 갭: 74회
74회. 5번이 1052회에 나오고, 다음에 나온 게 1126회. 약 1년 반 동안 안 나왔다.
갭 분포를 그려보면 이렇다:

갭 1~3회(금방 또 나옴)가 전체의 42%. 대부분 금방 다시 나온다. 하지만 가끔, 아주 가끔, 74회처럼 극단적인 공백이 있다.
이 분포가 "나올 때 됐다" 전략의 핵심 전제에 의문을 던진다.
핵심 질문: 오래 안 나왔으면 다음에 나올 확률이 올라가나?
이걸 검증하려면 **자기상관(autocorrelation)**을 봐야 한다.
쉽게 말하면:
"이번에 갭이 길었으면, 다음 갭은 짧아지는가?"
만약 "나올 때 됐다"가 맞다면, 긴 갭 뒤에 짧은 갭이 와야 한다. 자기상관이 음수여야 한다.
# 자기상관 계산
for n in range(1, 46):
gaps = get_gaps(n)
autocorr = np.corrcoef(gaps[:-1], gaps[1:])[0,1]
autocorrelations.append(autocorr)
print(f"평균 자기상관: {np.mean(autocorrelations):.4f}")
평균 자기상관: -0.0786
-0.08. 거의 0이다.

45개 번호의 자기상관을 차트로 그려봤다. 빨간 점선이 ±0.1 기준선인데, 대부분 이 안에 들어온다.
이전 갭이 길었다고 해서 다음 갭이 짧아지지 않는다. 이전 갭이 짧았다고 해서 다음 갭이 길어지지 않는다.
각 추첨은 독립이다. 로또 공은 "나 오래 쉬었으니까 이번엔 나갈게" 같은 생각을 하지 않는다.
그래도 안 믿기시죠? 백테스트를 돌려봤습니다
"자기상관이 0이다"는 이론적 결론이다. 실전에서도 그런지, 200회~1209회 구간에서 백테스트를 돌려봤다.
두 가지 전략을 비교했다:
전략 A: "나올 때 됐다" — 최근 가장 오래 안 나온 번호 6개 선택 전략 B: "또 나온다" — 최근 가장 자주 나온 번호 6개 선택 기준: 랜덤 — 아무 번호나 6개
# 백테스트 (200~1209회)
for test_round in range(200, 1210):
# 전략 A: 오래 안 나온 번호 TOP 6
gaps = get_consecutive_absence(df, test_round)
strategy_a = sorted(gaps, key=gaps.get, reverse=True)[:6]
# 전략 B: 최근 50회 최다 출현 TOP 6
recent = df[(df['round'] > test_round - 50) & (df['round'] <= test_round)]
freq = count_frequency(recent)
strategy_b = sorted(freq, key=freq.get, reverse=True)[:6]
# 다음 회 당첨번호와 비교
actual = get_winning_numbers(test_round + 1)
hits_a = len(set(strategy_a) & set(actual))
hits_b = len(set(strategy_b) & set(actual))
결과:
전략 평균 적중 랜덤 대비 통계적 유의성
| 나올 때 됐다 | 0.792개 | -1.0% | p=0.38 (없음) |
| 최근 또 나온다 | 0.816개 | +2.0% | p=0.27 (없음) |
| 랜덤 기대값 | 0.800개 | 기준 | - |
"나올 때 됐다" 전략은 랜덤보다 오히려 0.8% 낮았다. "최근 또 나온다" 전략도 랜덤 대비 +2%지만 p=0.27이라 우연의 범위.
1,000회 이상의 백테스트에서 두 전략 모두 랜덤과 통계적으로 유의미한 차이가 없다.
이름이 있다: 도박사의 오류 (Gambler's Fallacy)
"나올 때 됐다"에는 이미 학술적 이름이 붙어 있다.
도박사의 오류(Gambler's Fallacy) — 독립 사건에서 과거 결과가 미래 확률에 영향을 준다는 착각.
동전을 5번 던져서 앞면이 5번 나왔다. 다음에 뒷면이 나올 확률은?
정답은 여전히 **50%**다.
동전은 "나 앞면 5번이나 나왔으니까 이번엔 뒷면 할게" 같은 생각을 하지 않는다. 로또 공도 마찬가지다.
솔직한 결론
서비스에 "신뢰도 70%"라고 써놓은 이 전략.
1,213회 데이터로 검증한 결과:
- 자기상관 ≈ 0 (이전 갭이 다음 갭에 영향 없음)
- 백테스트 적중률: 랜덤 대비 -1.0% (p=0.38)
- 통계적 유의성: 없음
70%? 솔직히 말하면, 이 전략의 실제 신뢰도는 랜덤과 같다.
신뢰도: 70% → 실제: 랜덤
그래도 이 전략이 서비스에 존재하는 이유는... 사람들이 이걸 원하기 때문이다.
"나올 때 됐다"는 인간의 본능적인 패턴 인식이다. 틀렸다는 걸 알면서도 쓰고 싶은 전략이다. 그래서 넣어뒀다. 솔직하게.
다음 편 예고
🔥 #5 "또 나온다" — 빈도 균형 전략의 알고리즘
"27번이 최근 8회 중 6회 나왔다. 75%. 정상의 5.8배." 이건 진짜 핫한 건가, 우연인가? 신뢰도 78%라고 써놨는데... 다음 편에서 까본다.
'프로그래밍 > AI' 카테고리의 다른 글
| [Python] 로또 분석 서비스를 만들어봤다 — 10가지 전략의 알고리즘을 공개합니다 #3 (0) | 2026.03.07 |
|---|---|
| 파인튜닝 인생과 참을 수 있을지 없을지 모르는 존재의 가벼움 (0) | 2026.03.02 |