이 글은 패스트캠퍼스 바이브코딩 강의를 수강하며, 별도의 주제로 진행한 데이터 분석 프로젝트 과정을 기록한 것입니다. 코딩과 글 작성에는 클로드코드와 커서AI 및 퍼플렛시티를 함께 활용했음을 미리 밝힙니다.
Episode 5: 일곱 가지 선택의 기로 (Seven Choices, One Crossroad)
7가지 번호 추천 전략 구현: 하이브리드 추천 시스템 설계
점수 기반, 확률 가중치, 패턴, 그리드, 연속 번호, 무작위... 그리고 모든 것을 통합한 하이브리드. 일곱 가지 길이 펼쳐졌다.
작성일: 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단계 프로세스:
- Step 1: Score-based (10개 생성)
- Step 2: Probability (10개 생성)
- Step 3: Pattern (10개 생성)
- Step 4: Grid (10개 생성)
- 통합 및 중복 제거 (약 25-30개)
- 재점수 계산 및 정렬
- 상위 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 기반 재현성
흥미로운 발견
- 하이브리드가 최고 점수 (평균 475.3점)
- 14번이 68% 추천 (100개 조합 중)
- 패턴 보너스 평균 130점 (개별 점수의 약 37%)
- 무작위는 약 50점 낮음 (425.8점)
- 중복 제거 후 약 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}")
🔗 관련 링크
- GitHub: lotter645_1227
- Streamlit App: 로또 645 분석 웹 앱
- 이전 에피소드: 4편 - 기계가 배우는 운의 법칙
- 다음 에피소드: 6편 - 브라우저에 피어난 분석
💬 마무리하며
"일곱 가지 길이 펼쳐졌다."
점수 기반, 확률 가중치, 패턴, 그리드, 연속 번호, 무작위... 각각의 장점을 모았다. 그리고 하이브리드로 통합했다.
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%)
'VibeCoding > lo645251227' 카테고리의 다른 글
| Episode 3: 시간은 흐르고, 데이터는 남고 (Time Flows, Data Remains) (0) | 2026.01.11 |
|---|---|
| Episode 4: 기계가 배우는 운의 법칙 (The Machine's Fortune: Learning Rules) (0) | 2026.01.11 |
| Episode 6: 브라우저에 피어난 분석 (Browser-Based Analysis) (1) | 2026.01.11 |
| Episode 7: 8501 포트 너머로 (Beyond Port 8501) (0) | 2026.01.11 |
| Episode 8: 복사하고, 붙여넣고, 3초 (Copy, Paste, 3 Seconds) (0) | 2026.01.11 |



