파이썬으로 로또 역대 번호 통계 분석 프로그램 만들기

파이썬으로 만든 로또 프로그램

역대 로또 당첨 번호를 분석해 출현 횟수, 최근 출현 간격, 홀짝 비율을 계산하고 이를 바탕으로 번호 조합을 생성하는 파이썬 프로그램을 만들 수 있습니다. 다만 과거 통계가 다음 회차의 당첨 확률을 실제로 높여 주는 것은 아닙니다. 이 프로그램은 예측기라기보다 데이터 분석과 조합 생성 도구로 보는 것이 정확합니다.

먼저 CSV 파일을 준비합니다

회차별 번호를 CSV 파일에 저장합니다. 열 이름은 round,n1,n2,n3,n4,n5,n6으로 맞추고, 각 행에는 한 회차의 본번호 6개만 입력합니다. 보너스 번호는 조합 분석 대상에서 제외합니다.

round,n1,n2,n3,n4,n5,n6
1,10,23,29,33,37,40
2,9,13,21,25,32,42

실제 데이터를 넣을 때는 중복 번호, 1~45 범위를 벗어난 값, 번호가 6개보다 적거나 많은 행이 없는지 먼저 확인하세요. 데이터 오류가 있으면 통계 결과도 함께 왜곡됩니다.

통계 계산과 조합 생성을 한 번에 실행하기

아래 코드는 CSV를 읽은 뒤 번호별 출현 횟수, 최근 출현 회차, 평균 출현 간격을 계산합니다. 이후 출현 횟수에 비례한 가중치를 사용해 후보 번호를 뽑고, 홀짝 비율과 번호 합계가 지나치게 치우치지 않은 조합만 남깁니다.

import csv
import random
from collections import Counter
from statistics import mean

CSV_FILE = "lotto_history.csv"
NUMBER_RANGE = range(1, 46)


def load_history(filename):
    history = []
    with open(filename, newline="", encoding="utf-8-sig") as file:
        reader = csv.DictReader(file)
        required = ["round", "n1", "n2", "n3", "n4", "n5", "n6"]

        if not reader.fieldnames or any(col not in reader.fieldnames for col in required):
            raise ValueError("CSV 열 이름은 round,n1,n2,n3,n4,n5,n6이어야 합니다.")

        for row in reader:
            numbers = [int(row[f"n{i}"]) for i in range(1, 7)]
            if len(set(numbers)) != 6 or any(number not in NUMBER_RANGE for number in numbers):
                raise ValueError(f"잘못된 번호가 포함된 회차: {row['round']}")
            history.append({"round": int(row["round"]), "numbers": numbers})

    if not history:
        raise ValueError("분석할 데이터가 없습니다.")

    return sorted(history, key=lambda item: item["round"])


def calculate_statistics(history):
    frequency = Counter()
    appearances = {number: [] for number in NUMBER_RANGE}

    for draw in history:
        for number in draw["numbers"]:
            frequency[number] += 1
            appearances[number].append(draw["round"])

    latest_round = history[-1]["round"]
    stats = {}

    for number in NUMBER_RANGE:
        rounds = appearances[number]
        gaps = [b - a for a, b in zip(rounds, rounds[1:])]
        stats[number] = {
            "count": frequency[number],
            "last_seen": rounds[-1] if rounds else None,
            "gap": latest_round - rounds[-1] if rounds else latest_round,
            "average_gap": mean(gaps) if gaps else None,
        }

    return stats


def print_statistics(stats):
    print("번호 | 출현 | 최근 간격 | 평균 간격")
    print("-" * 36)
    for number, data in stats.items():
        average_gap = f"{data['average_gap']:.2f}" if data["average_gap"] else "-"
        print(
            f"{number:>4} | {data['count']:>4} | "
            f"{data['gap']:>8} | {average_gap:>8}"
        )


def generate_candidate(stats, history_count, attempts=10000):
    # 출현 횟수가 많을수록 선택 가중치를 조금 높입니다.
    # 모든 번호가 선택될 수 있도록 최소 가중치를 둡니다.
    weights = [1 + stats[number]["count"] for number in NUMBER_RANGE]
    numbers = list(NUMBER_RANGE)
    candidates = []

    for _ in range(attempts):
        selected = sorted(random.choices(numbers, weights=weights, k=6))
        if len(set(selected)) != 6:
            continue

        odd_count = sum(number % 2 for number in selected)
        total = sum(selected)

        # 특정 통계 조건에 맞는 후보만 저장합니다.
        if 2 <= odd_count <= 4 and 100 <= total <= 200:
            score = sum(stats[number]["count"] for number in selected)
            candidates.append((score, selected))

    if not candidates:
        raise RuntimeError("조건에 맞는 조합을 만들지 못했습니다.")

    candidates.sort(reverse=True)
    return candidates[0][1]


if __name__ == "__main__":
    history = load_history(CSV_FILE)
    stats = calculate_statistics(history)

    print(f"분석 회차 수: {len(history)}")
    print_statistics(stats)

    candidate = generate_candidate(stats, len(history))
    print("\n통계 기반 후보 조합:", candidate)
    print("홀수 개수:", sum(number % 2 for number in candidate))
    print("번호 합계:", sum(candidate))

실행 방법과 결과 해석

  • 코드 파일과 lotto_history.csv를 같은 폴더에 저장합니다.
  • 터미널에서 해당 폴더로 이동합니다.
  • python lotto_analysis.py 명령을 실행합니다.
  • 번호별 출현 횟수와 최근 간격을 확인한 뒤 마지막에 후보 조합을 확인합니다.

출현 횟수는 전체 데이터에서 해당 번호가 몇 번 등장했는지를 뜻합니다. 최근 간격은 마지막으로 등장한 회차와 현재 데이터의 마지막 회차 사이의 차이입니다. 평균 간격은 해당 번호가 연속으로 등장한 회차 차이의 평균이므로, 아직 출현 기록이 한 번뿐인 번호에는 계산되지 않습니다.

코드의 100~200 합계 조건이나 홀수 2~4개 조건은 당첨을 보장하는 규칙이 아닙니다. 원하는 분석 기준에 따라 조건을 바꿀 수 있지만, 조건을 많이 추가할수록 통계적 근거보다 임의의 필터에 가까워질 수 있습니다.

자주 막히는 오류

오류 상황확인할 부분
UnicodeDecodeErrorCSV가 UTF-8 형식인지 확인합니다. 코드에는 UTF-8 BOM을 처리하는 utf-8-sig가 사용되어 있습니다.
CSV 열 이름 오류round,n1,n2,n3,n4,n5,n6이 정확히 일치하는지 확인합니다.
잘못된 번호 오류각 행에 서로 다른 1~45 번호 6개가 있는지 확인합니다.
후보 조합 생성 실패합계 범위나 홀짝 조건이 지나치게 좁은지 확인하고 attempts 값을 늘립니다.

이 프로그램의 한계

로또 추첨은 각 회차가 독립적으로 진행되므로 특정 번호가 과거에 자주 나왔거나 오래 나오지 않았다는 사실만으로 다음 회차 확률이 달라진다고 볼 수 없습니다. 위 코드는 빈도에 가중치를 주기 때문에 ‘많이 나온 번호’를 선택할 가능성이 커질 뿐이며, 이것은 확률 예측 모델로 검증된 방식이 아닙니다.

핵심은 통계를 이용해 번호를 확정하는 것이 아니라, 데이터를 정리하고 기준별 결과를 재현하는 것입니다. 실제 구매 여부와 금액은 별도로 판단하고, 프로그램 결과를 당첨 보장이나 투자 판단으로 해석하지 마세요.