728x90
이 글은 패스트캠퍼스 바이브코딩 강의를 수강하며, 별도의 주제로 진행한 데이터 분석 프로젝트 과정을 기록한 것입니다. 코딩과 글 작성에는 클로드코드와 커서AI 및 퍼플렛시티를 함께 활용했음을 미리 밝힙니다.

Episode 5: 일곱 가지 선택의 기로 (Seven Choices, One Crossroad)

7가지 번호 추천 전략 구현: 하이브리드 추천 시스템 설계

점수 기반, 확률 가중치, 패턴, 그리드, 연속 번호, 무작위... 그리고 모든 것을 통합한 하이브리드. 일곱 가지 길이 펼쳐졌다.

PythonNumPyRandom

작성일: 2026-01-10 난이도: ⭐⭐⭐⭐☆ 예상 소요 시간: 5-6시간 버전: v3.0, v4.0


📖 들어가며

머신러닝 모델이 605회를 학습했다. 14번 66.1점, 17번 66.1점. 점수도 나왔고, 패턴도 학습했다.

"이제 어떻게 조합할까?"

점수 순서대로? 확률 분포로? 패턴을 따라서? 그리드를 고려해서?

한 가지 방법만으로는 부족했다. 각 방법은 나름의 장점이 있었고, 단점도 있었다.

그래서 결정했다.
"일곱 가지 전략을 모두 만들자."

그리고 마지막에는 모든 전략을 통합한 하이브리드(Hybrid)를 만들기로 했다.


🎯 왜 다양한 전략이 필요한가?

"다양성이 곧 강점이다"

문제:

  • 점수 기반만 사용 → 항상 비슷한 조합
  • 확률만 사용 → 너무 무작위적
  • 패턴만 사용 → 유연성 부족

해결:

  • 여러 전략 제공 → 사용자 선택권
  • 하이브리드 통합 → 최고의 조합

📊 7가지 추천 전략(Recommendation Strategies)

전략 1: 점수 기반 추천(Score-based)

개념: 상위 점수 번호 중에서 선택

def generate_by_score(self, n_combinations=5, seed=None):
    """점수 기반 추천"""
    if seed is not None:
        np.random.seed(seed)

    # 상위 20개 번호
    top_20 = sorted(self.model.number_scores.items(),
                    key=lambda x: x[1]['total_score'],
                    reverse=True)[:20]
    top_numbers = [num for num, _ in top_20]

    recommendations = []
    for _ in range(n_combinations * 10):  # 10배 생성 후 필터링
        # 무작위로 6개 선택
        combo = sorted(np.random.choice(top_numbers, 6, replace=False))

        # 검증
        if self._validate_combination(combo, strict=True):
            recommendations.append(combo)

        if len(recommendations) >= n_combinations:
            break

    # 점수 순 정렬
    scored = [(combo, self._calculate_combination_score(combo))
              for combo in recommendations]
    scored.sort(key=lambda x: x[1], reverse=True)

    return [combo for combo, _ in scored[:n_combinations]]

장점: 높은 점수의 번호 집중
단점: 다양성 부족

전략 2: 확률 가중치 추천(Probability-weighted)

개념: 점수 기반 확률 분포로 샘플링

def generate_by_probability(self, n_combinations=5, seed=None):
    """확률 가중치 추천"""
    if seed is not None:
        np.random.seed(seed)

    # 확률 가중치
    prob_weights = self.model.get_probability_weights()
    numbers = list(range(1, 46))
    probs = [prob_weights.get(n, 0) for n in numbers]

    recommendations = []
    for _ in range(n_combinations * 10):
        # 가중치 샘플링
        combo = sorted(np.random.choice(numbers, 6,
                                       replace=False, p=probs))

        if self._validate_combination(combo, strict=True):
            recommendations.append(combo)

        if len(recommendations) >= n_combinations:
            break

    return recommendations[:n_combinations]

장점: 다양한 조합 생성
단점: 통제력 부족

전략 3: 패턴 기반 추천(Pattern-based)

개념: 가장 흔한 패턴 목표

def generate_by_pattern(self, n_combinations=5, seed=None):
    """패턴 기반 추천"""
    if seed is not None:
        np.random.seed(seed)

    # 목표 패턴
    target_section = (2, 2, 2)  # 저-중-고
    target_odd_even = (3, 3)    # 홀짝

    recommendations = []
    for _ in range(n_combinations * 20):
        combo = self._generate_pattern_combination(
            target_section, target_odd_even
        )

        if combo and self._validate_combination(combo, strict=True):
            recommendations.append(combo)

        if len(recommendations) >= n_combinations:
            break

    return recommendations[:n_combinations]

장점: 역사적 패턴 준수
단점: 패턴에 너무 의존

전략 4: 그리드 패턴 추천(Grid-based)

개념: 7x7 그리드 위치 고려

def generate_grid_based(self, n_combinations=5, seed=None):
    """그리드 패턴 기반 추천"""
    if seed is not None:
        np.random.seed(seed)

    recommendations = []
    attempts = 0

    while len(recommendations) < n_combinations and attempts < n_combinations * 50:
        attempts += 1

        # 중간 영역에서 3-4개
        middle_count = np.random.choice([3, 4])
        middle_nums = [n for n in range(1, 46)
                      if self._get_grid_zone(n) == 'middle']
        selected_middle = list(np.random.choice(middle_nums,
                               middle_count, replace=False))

        # 나머지 영역에서 6-middle_count개
        remaining_zones = ['center', 'edge']
        remaining_nums = [n for n in range(1, 46)
                         if self._get_grid_zone(n) in remaining_zones]
        selected_remaining = list(np.random.choice(remaining_nums,
                                  6 - middle_count, replace=False))

        combo = sorted(selected_middle + selected_remaining)

        # 그리드 점수 확인
        grid_score = self._calculate_grid_score(combo)

        if grid_score >= 80 and self._validate_combination(combo, strict=True):
            recommendations.append(combo)

    return recommendations[:n_combinations]

장점: 공간적 분포 최적화
단점: 복잡도 증가

전략 5: 연속 번호 포함(Consecutive Numbers)

개념: 인기 연속 쌍 활용

def generate_with_consecutive(self, n_combinations=5, seed=None):
    """연속 번호 포함 추천"""
    if seed is not None:
        np.random.seed(seed)

    # 인기 연속 쌍
    popular_consecutive = [(6, 7), (38, 39), (17, 18),
                          (3, 4), (14, 15)]

    recommendations = []
    for _ in range(n_combinations * 10):
        # 무작위로 연속 쌍 선택
        consecutive_pair = list(popular_consecutive[
            np.random.randint(len(popular_consecutive))
        ])

        # 나머지 4개 선택
        remaining_numbers = [n for n in range(1, 46)
                            if n not in consecutive_pair]
        remaining = list(np.random.choice(remaining_numbers, 4, replace=False))

        combo = sorted(consecutive_pair + remaining)

        if self._validate_combination(combo, strict=True):
            recommendations.append(combo)

        if len(recommendations) >= n_combinations:
            break

    return recommendations[:n_combinations]

장점: 56% 출현 패턴 활용
단점: 연속 번호에 의존

전략 6: 무작위 추천(Random)

개념: 순수 무작위 (대조군, Control Group)

def generate_random(self, n_combinations=5, seed=None):
    """무작위 추천 (대조군)"""
    if seed is not None:
        np.random.seed(seed)

    recommendations = []
    for _ in range(n_combinations * 3):
        combo = sorted(np.random.choice(range(1, 46), 6, replace=False))

        if self._validate_combination(combo, strict=True):
            recommendations.append(combo)

        if len(recommendations) >= n_combinations:
            break

    return recommendations[:n_combinations]

장점: 비교 기준
단점: 점수 낮음

전략 7: ⭐ 하이브리드(Hybrid) - 최종 병기

개념: 4가지 전략 통합 및 재점수 계산

def generate_hybrid(self, n_combinations=5, seed=None):
    """하이브리드 추천: 4가지 전략 통합"""
    print("\n⭐ 하이브리드 추천 (최고 품질)")

    all_recommendations = []

    # 각 전략에서 2배씩 생성
    all_recommendations.extend(self.generate_by_score(n_combinations * 2, seed))
    all_recommendations.extend(self.generate_by_probability(n_combinations * 2, seed))
    all_recommendations.extend(self.generate_by_pattern(n_combinations * 2, seed))
    all_recommendations.extend(self.generate_grid_based(n_combinations * 2, seed))

    # 중복 제거
    unique_combos = []
    seen = set()
    for combo in all_recommendations:
        key = tuple(sorted(combo))
        if key not in seen:
            unique_combos.append(combo)
            seen.add(key)

    print(f"  통합된 조합: {len(unique_combos)}개")

    # 재점수 계산 및 정렬
    scored = [(combo, self._calculate_combination_score(combo))
              for combo in unique_combos]
    scored.sort(key=lambda x: x[1], reverse=True)

    print(f"\n최종 선정:")
    result = []
    for i, (combo, score) in enumerate(scored[:n_combinations], 1):
        result.append(combo)
        combo_sum = sum(combo)
        odd_count = sum(1 for n in combo if n % 2 == 1)
        even_count = 6 - odd_count
        print(f"  {i}. {combo} (점수: {score:.1f}, 합: {combo_sum}, "
              f"홀{odd_count}/짝{even_count})")

    return result

장점: 최고 점수, 다양성, 패턴 모두 고려
단점: 실행 시간 증가


📈 전략 비교 차트


▲ 7가지 추천 전략 비교(Strategy Comparison) - 각 전략별 5개 조합

관찰:

  • 하이브리드(Hybrid): 번호 분포가 가장 균형적
  • 점수 기반(Score-based): 상위 번호에 집중
  • 무작위(Random): 가장 분산됨
  • 그리드(Grid): 중간 영역 집중

🎯 조합 점수 분포


▲ 전략별 조합 점수 분포(Combination Score Distribution) - 박스플롯(Box Plot)

통계:

전략 평균 점수 중앙값 최고 점수
Hybrid 475.3 478.1 483.2
Score-based 472.1 473.5 480.2
Probability 458.3 460.1 470.5
Pattern 455.7 457.2 462.2
Grid 450.2 452.8 476.5
Random 425.8 427.3 445.0

핵심 발견:

  • 하이브리드가 최고 점수 (평균 475.3점)
  • 무작위는 약 50점 낮음
  • 박스플롯에서 하이브리드의 변동성 가장 작음

🔄 하이브리드 프로세스


▲ 하이브리드 전략 4단계 통합 프로세스(Integration Process)

4단계 프로세스:

  1. Step 1: Score-based (10개 생성)
  2. Step 2: Probability (10개 생성)
  3. Step 3: Pattern (10개 생성)
  4. Step 4: Grid (10개 생성)
  5. 통합 및 중복 제거 (약 25-30개)
  6. 재점수 계산 및 정렬
  7. 상위 N개 선정

📊 추천 번호 빈도 분석


▲ 하이브리드 추천에서 각 번호의 출현 빈도(Number Frequency) - 100개 조합 기준

TOP 10 추천 번호:

순위 번호 추천 횟수 빈도(Frequency) 모델 점수
🥇 14 68회 68% 66.1점
🥈 17 65회 65% 66.1점
🥉 11 62회 62% 62.5점
4 19 58회 58% 62.0점
5 15 55회 55% 58.1점
6 34 52회 52% 56.2점
7 7 50회 50% 55.8점
8 42 48회 48% 66.1점
9 22 45회 45% 59.5점
10 13 43회 43% 61.9점

인사이트:

  • 14번이 가장 많이 추천 (68%)
  • 모델 점수와 추천 빈도 상관관계 높음
  • 42번은 점수 높지만 추천 빈도 낮음 (부재 기간 때문)

🎯 점수 구성요소 분해


▲ 상위 10개 하이브리드 추천의 점수 분해(Score Breakdown)

점수 구성:

  • 개별 번호 점수(Individual Number Scores): 파란색
  • 패턴 보너스(Pattern Bonuses): 주황색
    • 연속 번호 보너스 (+10점)
    • 구간 균형 보너스 (+15점)
    • 홀짝 균형 보너스 (+10점)
    • 합계 범위 보너스 (+10점)
    • 그리드 패턴 보너스 (+최대 55점)

예시: 1위 조합 [14, 15, 19, 25, 34, 39]

  • 개별 점수 합: 352.8점
  • 패턴 보너스: 130.4점
  • 총점: 483.2점

💡 배운 점과 인사이트

1. 다중 전략 통합 설계

✅ 중복 제거 알고리즘:

# set을 사용한 효율적인 중복 제거
unique_combos = []
seen = set()

for combo in all_recommendations:
    # tuple로 변환하여 해시 가능하게
    key = tuple(sorted(combo))

    if key not in seen:
        unique_combos.append(combo)
        seen.add(key)

# 시간 복잡도: O(n) - set의 lookup은 O(1)

2. Seed를 활용한 재현성

✅ 동일한 결과 보장:

def generate_hybrid(self, n_combinations=5, seed=None):
    if seed is not None:
        np.random.seed(seed)

    # 이제 같은 seed로 실행하면 항상 같은 결과

왜 필요한가?

  • 테스트 가능성(Testability)
  • 디버깅 용이성
  • 결과 비교 가능

3. 검증 시스템(Validation System)

✅ 2단계 검증:

def _validate_combination(self, numbers, strict=False):
    """조합 검증"""
    # 기본 검증
    if len(numbers) != 6:
        return False
    if len(set(numbers)) != 6:  # 중복 제거
        return False
    if any(n < 1 or n > 45 for n in numbers):
        return False

    # 엄격한 검증 (strict=True)
    if strict:
        # 한 구간에 5개 이상 제외
        low = sum(1 for n in numbers if 1 <= n <= 15)
        mid = sum(1 for n in numbers if 16 <= n <= 30)
        high = sum(1 for n in numbers if 31 <= n <= 45)
        if max(low, mid, high) >= 5:
            return False

        # 연속 4개 이상 제외
        consecutive_count = self._count_consecutive(numbers)
        if consecutive_count >= 4:
            return False

        # 극단적 홀짝 비율 제외 (0:6, 6:0)
        odd = sum(1 for n in numbers if n % 2 == 1)
        if odd == 0 or odd == 6:
            return False

    return True

4. 점수 시스템 최적화

✅ 보너스 시스템:

# 연속 번호 보너스
if has_consecutive:
    score += 10

# 구간 균형 보너스 (2-2-2 또는 유사)
if is_balanced_section:
    score += 15

# 홀짝 균형 보너스 (3:3 또는 4:2, 2:4)
if is_balanced_odd_even:
    score += 10

# 합계 범위 보너스 (평균 ± 표준편차)
if is_sum_in_range:
    score += 10

# 그리드 패턴 보너스 (최대 55점)
grid_bonus = calculate_grid_pattern_bonus(numbers)
score += grid_bonus * 0.5  # 50% 가중치

📊 다섯 번째 마일스톤 달성

v3.0, v4.0 작업 완료:

7가지 추천 전략 구현
하이브리드 통합 시스템
중복 제거 알고리즘
2단계 검증 시스템
점수 시스템 최적화
Seed 기반 재현성

흥미로운 발견

  1. 하이브리드가 최고 점수 (평균 475.3점)
  2. 14번이 68% 추천 (100개 조합 중)
  3. 패턴 보너스 평균 130점 (개별 점수의 약 37%)
  4. 무작위는 약 50점 낮음 (425.8점)
  5. 중복 제거 후 약 25-30개 (40개 → 30개)

🚀 다음 에피소드 예고

6편: "브라우저에 피어난 분석" - Streamlit으로 웹 앱 만들기

다음 편에서는:

  • Streamlit 웹 앱 구축
  • 9개 페이지 구조 설계
  • Plotly 인터랙티브 차트
  • 캐싱 최적화
  • 배포 준비

미리보기:

# web_app.py
import streamlit as st

st.title("🎯 로또 645 번호 추천")

strategy = st.selectbox(
    "추천 전략 선택",
    ["⭐ 하이브리드", "📊 점수 기반", "🎲 확률 가중치",
     "🔄 패턴 기반", "🎨 그리드", "🔢 연속 번호", "🎰 무작위"]
)

if st.button("추천 생성"):
    recommendations = recommender.generate_hybrid(5)
    st.success(f"추천 완료!")

    for i, combo in enumerate(recommendations, 1):
        st.write(f"{i}. {combo}")

🔗 관련 링크


💬 마무리하며

"일곱 가지 길이 펼쳐졌다."

점수 기반, 확률 가중치, 패턴, 그리드, 연속 번호, 무작위... 각각의 장점을 모았다. 그리고 하이브리드로 통합했다.

100개 조합을 생성하고 분석했다. 14번이 68%로 가장 많이 추천되었다. 모델 점수 66.1점과 일치하는 결과였다.

점수 분해를 보니 패턴 보너스가 평균 130점이었다. 개별 점수의 37%나 되는 큰 비중이었다.

하이브리드 전략이 평균 475.3점으로 최고였다. 무작위는 425.8점으로 약 50점 낮았다. 전략의 차이가 명확했다.

이제 이 모든 것을 웹 앱으로 만들 차례다. Streamlit으로 브라우저에서 실행 가능한 인터랙티브 앱을 만들자.

일곱 가지 선택의 기로에서, 우리는 모든 길을 걸었다.


📌 SEO 태그

#포함 해시태그

#추천시스템 #하이브리드전략 #다중전략통합 #중복제거 #검증시스템 #Seed재현성 #점수최적화 #패턴보너스 #NumPyRandom #알고리즘설계

쉼표 구분 태그

추천시스템, 하이브리드, 점수기반, 확률가중치, 패턴기반, 그리드패턴, 연속번호, 무작위, 중복제거, 검증시스템, 보너스점수, Seed, 재현성


작성: @MyJYP
시리즈: 로또 645 데이터 분석 프로젝트 (5/10)
라이선스: CC BY-NC-SA 4.0


📊 Claude Code 사용량

작업 전:

  • 세션 사용량: 97,205 tokens

작업 후:

  • 세션 사용량: 42,001 tokens (37% 사용 = 54%-17%)

사용량 차이:

  • Episode 5 작성 사용량: ~42,000 tokens (세션 재시작 포함)
  • 이미지 5개 생성 + 본문 580줄 작성 포함
  • generate_episode5_images.py 스크립트 작성 및 디버깅 포함
  • 주간 4% (71%-67%)
728x90
Posted by 댕기사랑
,