public List<int[]> recommend(List lottoData, int count, String ratioOption) {
List<int[]> recommendations = new ArrayList<>();
Map<Integer, Integer> freq = new HashMap<>();
// 1️⃣ 전체 번호 출현 횟수 세기
for (LottoResult result : lottoData) {
for (int num : result.getNumberOfRound()) {
freq.put(num, freq.getOrDefault(num, 0) + 1);
}
}
// 2️⃣ 많이 나온 번호 Top 20 구하기
List<Integer> topNumbers = freq.entrySet().stream()
.sorted((a, b) -> b.getValue() - a.getValue())
.limit(20)
.map(Map.Entry::getKey)
.toList();
.collect(Collectors.toList());
// 3️⃣ 조건 만족하는 추천 번호 세트 만들기
Random random = new Random();
while (recommendations.size() < count) {
Collections.shuffle(topNumbers);
int[] candidate = topNumbers.stream()
.limit(6)
.mapToInt(Integer::intValue)
.toArray();
if (isValidCombination(candidate, ratioOption)) {
Arrays.sort(candidate); // 보기 좋게 정렬
recommendations.add(candidate);
}
}
return recommendations;
}
List<int[]> recommendations = new ArrayList<>();
-> 추천된 로또 번호 세트들 담을 리스트
List<int[]>
: 리스트인데 각 요소는 int[] 로또 번호 6개짜리 배열
new ArrayList<>()
: 비어있는 리스트를 새로 만듦
인터페이스타입 변수명 = new 구현클래스
인터페이스 타입 : 기능만 약속, 구현은 하지 않음 (List, Map, Set, Runnable)
구현 클래스 : 인터페이스에서 약속된 기능을 구현하는 클래스 (ArrayList, LinkedLish | HashMap, TreeMap | HashSet, TreeSet | 직접 구현한 클래스)
Map<Integer, Integer> freq = new HashMap<>();
-> 과거 로또 번호 중에서 각 숫자가 몇 번 나왔는지 세는 코드
Map
: 키(숫자)와 값(빈도)을 쌍으로 저장하는 자료형
<Integer, Integer>
: 키도 정수형, 값도 정수형
freq
:변수명, frequency(빈도)의 약자
new HashMap<>()
: 빈 해시맵 생성
for (LottoResult result : lottoData) {
~
}
lottoData의 값을 하나씩 꺼내서 result에 넣어줌
for(타입 변수 : 배열/ 리스트)
for (int num : result.getNumberOfRound()) {
freq.put(num, freq.getOrDefault(num, 0) + 1);
}
result.getNumberOfRound()
: 그 회차의 당첨 번호(result)를 6개 배열로 만들어 int[] 배열로 리턴(getNumberOfRound())하는 메서드
int num : result.getNumberOfRound()
: 당첨 번호 6개 배열 속 숫자를 하나씩 num에 넣는다
freq.put(num, freq.getOrDefault(num, 0) + 1);
:Map인 freq에 num이 몇번 나왔는지 누적해서 세는 코드
freq.put(키, 값)
: freq에 데이터를 저장해라
키가 있으면 값을 덮어씀(업데이트)
키 없으면 값을 새로 추가함
freq.getOrDefault(num, 0) + 1
: freq에 num 있으면 num에 해당하는 값(빈도) 리턴하고
없으면 0 리턴
리턴한 값에 1 더함
getOrDefault(a,b)
:java에서 map에 기본으로 내장되어 있는 메서드
a 가 있으면 a에 해당하는 값을 리턴하고 없으면 b를 리턴해라
List topNumbers = freq.entrySet().stream()
.sorted((a, b) -> b.getValue() - a.getValue())
.limit(20)
.map(Map.Entry::getKey)
.toList();
freq라는 map 안에서 출현 횟수가 많은 번호 20개 뽑아서 리스트로 저장하는 코드
freq.entrySet().stream()
: (key, value) 쌍을 세트로 꺼낸 결과(entrySet()
ex){(3,12),(12,7)...})를 stream으로 바꿔줌
stream으로 바꿔주어서 다음 코드 앞에 freq를 안붙여주고 .sorted... .limit 이런식으로 붙여줄 수 있었음
.sorted((a, b) -> b.getValue() - a.getValue())
: Map.Entry 인 a,b 두개(a,b)를 비교해서 출현 횟수가 큰 번호가 먼저 오도록 정렬(b.getValue() - a.getValue())
.getValue() : 그 번호가 몇번 나왔는지
.sorted : 음수면 앞에 양수면 뒤로 정렬
b.getValue() - a.getValue() : 내림차순
a.getValue() - b.getValue(): 오름차순
.limit(20)
: 앞에서 20개만 남아라
sorted(...) 으로 출현 횟수가 많은 순서로 정렬했으니 가장 많이 나온 20개만 뽑겠다는 뜻
.map(Map.Entry::getKey)
: (키, 값) 쌍에서 키만 꺼내라
.map(e -> e.getKey()) 랑 같은 뜻
.toList();
: 리스트로 만들어라
stream은 일회용이라 list 로 모아야 저장 가능
Random random = new Random();
:Random 자바에서 기본 제공 클래스
while (recommendations.size() < count) {
Collections.shuffle(topNumbers);
int[] candidate = topNumbers.stream()
.limit(6)
.mapToInt(Integer::intValue)
.toArray();
if (isValidCombination(candidate, ratioOption)) {
Arrays.sort(candidate); // 보기 좋게 정렬
recommendations.add(candidate);
}
}
: topNumbers 안에서 6개를 랜덤으로 뽑아서 조건(isValidCombination)을 통과하면 recommendations 리스트에 추가. 이걸 count 번 반복(나의 경우 10번)
Collections.shuffle(topNumbers)
:topNumbers 리스트의 순서를 셔플함
여기서 collections는 자바 기본 제공 클래스
.mapToInt(Integer::intValue)
: int[] 이 필요하기 때문에 중간에 mapToInt() 를 써서 Integer -> int 로 변환하는 단계 필요
.mapToInt() : stream에서 객체(integer)에서 기본 타입(int)로 바꿔주는 메서드
intValue() : integer 객체 안에 있는 int 값 꺼내는 메서드
.toArray()
: 스트림을 배열로 마무리
if (isValidCombination(candidate, ratioOption))
: candidate 라는 번호 조합이 ratioOption 라는 조건(ex) 홀짝) 을 만족하는지 거사하는 함수 호출
isValidCombination 라는 함수를 내가 만들어 줘야함
Array.sort(candidate)
: candidate 배열을 오름차순으로 정렬
recommendations.add(candidate)
: recommendations 리스트에 추가
조건검사 함수
private boolean isValidCombination(int[] candidate, String ratioOption) {
int odd = 0;
int even = 0;
int sum = 0;
for (int num : candidate) {
if (num % 2 == 0) even++;
else odd++;
sum += num;
}
// 홀짝 비율 검사
String[] ratio = ratioOption.split(":");
int expectedOdd = Integer.parseInt(ratio[0]);
int expectedEven = Integer.parseInt(ratio[1]);
if (odd != expectedOdd || even != expectedEven) {
return false;
}
// 합계 범위 검사
if (sum < 120 || sum > 160) {
return false;
}
return true;
}
odd : 번호 중 홀수 개수
even : 번호 중 짝수 개수
String[] ratio = ratioOption.split(":");
: .split(":") :를 기준으로 문자열 쪼개줌
int expectedOdd = Integer.parseInt(ratio[0]);
int expectedEven = Integer.parseInt(ratio[1]);
:문자열을 정수으로 변환해서 expectedOdd()에 저장
if (odd != expectedOdd || even != expectedEven) {
return false;
}
: 홀수, 짝수가 expected와 다르면 f 반환