언더독 뽑기 기능 구현
개요
기본적인 뽑기 기능 구현의 마지막 단계인 언더독 뽑기이다. 저번에 만든 기능이 지금까지 당첨된 번호들을 조회해 나온 확률을 기반으로 숫자 6개를 뽑아주는 기능이었다면, 이번에는 그 반대로 많이 나온 숫자일수록 나올 확률이 적고, 그동안 안 나온 숫자들이 나올 확률이 높아지도록 설정해서 숫자를 뽑는 기능이다.
방법론 탐색
처음 이 기능을 어떻게 구현할까 생각하면서 몇 가지 방법을 찾아봤다.
첫 번째 방법은 최대 확률에서 각 확률을 빼는 방법이었다. 두 번째 방법은 각 확률의 역수를 이용하는 방법이었다. 그러나 두 방법 모두 확률이 100%가 되지 않거나, 정규화를 통해 100%를 만든다 해도 실제 비율과의 차이가 커지는 문제점이 있었다.
첫 번째 방법
각 확률이 [10, 20, 30, 40]
인 배열이 있다고 가정하면:
- 첫 번째 방법은 각 확률을 최대 확률에서 빼는 방식으로 진행된다.
40 - 10 + 1 = 31
40 - 20 + 1 = 21
40 - 30 + 1 = 11
40 - 40 + 1 = 1
두 번째 방법
- 두 번째 방법은 확률의 역수를 이용한 방식이다.
1/10 = 0.1
1/20 = 0.05
1/30 = 0.0333
1/40 = 0.025
[48, 24, 16, 12]
의 배열이 나온다. 최소값과 최대값의 비율이 괜찮아 보일 수 있지만, 내가 원하는 결과는 간단하게 확률을 뒤집은[40, 30, 20, 10]
형태였다. 그래서 복잡하게 생각하지 않고, 각 확률을 뒤집어서 사용하기로 결정했다.
코드를 작성하기 전에 머릿속이 정리가 되지 않아 글로 정리하며 생각을 정리했다.
데이터 설명
내가 받는 데이터
- 1부터 45까지의 각 숫자에 대한 확률이 담긴 배열.
구현 과정
- 받은 배열을 각 숫자에 맞게 객체로 변환한다. (예:
const probabilityData = { num1: 10, num2: 20, num3: 30, num4: 40 }
) - 객체를 확률에 따라 내림차순으로 정렬하고, 확률 값만 배열로 추출한다.
- 추출한 데이터를 오름차순으로 정렬하여 다시 객체에 넣어준다.
- 객체를 숫자 순서에 따라 오름차순 정렬한다.
- 정렬된 객체에서 확률 값만 배열로 추출해 최종 뽑기 기능에 사용한다.
첫 번째 코드 구현
처음에는 위와 같이 풀어서 생각했고, 실제로 이렇게 코드를 작성해 보았다.
function underDogPickFunction(probabilityData) {
const numbers = [];
// 숫자와 확률을 객체 형태로 매핑
const probObject = probabilityData.map((prob, index) => ({
number: index + 1,
probability: prob,
}));
// 확률 데이터에 따라 객체 내림차순 정렬
const sortedData = probObject.sort((a, b) => b.probability - a.probability);
// 확률 값만 가져와서 오름차순 배치
const sortedProbabilities = sortedData
.map((item) => item.probability)
.sort((a, b) => a - b);
// 확률 값에 따라 내림차순 정렬된 숫자 객체 생성
const reversedData = sortedData.map((item, index) => ({
number: item.number,
probability: sortedProbabilities[index],
}));
// 1. number를 기준으로 오름차순 정렬
const sortedByNumber = reversedData.sort((a, b) => a.number - b.number);
// 2. 정렬된 데이터에서 probability 값만 추출
const probabilityArray = sortedByNumber.map((item) => item.probability);
// 뽑기에 이용할 배열 생성 및 확률 픽 기능 구현
const availableNumbers = Array.from(
{ length: probabilityArray.length },
(_, i) => i + 1
);
const totalProbability = probabilityArray.reduce(
(sum, prob) => sum + prob,
0
);
while (numbers.length < 6) {
const ranNum = Math.random() * totalProbability;
let cumulativeProbability = 0;
for (let i = 0; i < availableNumbers.length; i++) {
cumulativeProbability += probabilityArray[i];
if (ranNum <= cumulativeProbability) {
numbers.push(availableNumbers[i]);
availableNumbers.splice(i, 1);
break;
}
}
}
return numbers.sort((a, b) => a - b);
}
코드 개선 및 문제 해결
이렇게 코드를 작성하고 나니, 불필요한 부분이 있다는 느낌이 들었다. 배열을 만들고 정렬하는 과정에서 쓸모없는 부분이 있을 거라 확신했고, 이에 대해 고민해 보았다.
결국 크게 줄어들거나 수정된 부분은 없었지만, 숫자를 굳이 1부터 시작하려 해서 코드가 길어진 것이 문제였다. 또한 글을 쓰는 도중 뽑기 기능에 문제가 있음을 발견했다. 숫자를 뽑은 후 누적 확률을 초기화하지 않고 있었다는 문제였다.
이 문제 때문에 기능이 제대로 작동하지 않았지만, 문제 해결 자체는 데이터를 새로운 배열에 저장하고, 뽑은 후 해당 숫자와 그 확률을 제거하며 누적 확률을 초기화하는 방식으로 해결했다.
최종 코드
function underDogPickFunction(probabilityData) {
const numbers = [];
const probObject = probabilityData.map((prob, index) => ({
number: index + 1,
probability: prob,
}));
// 확률 데이터 내림차순 정렬
const sortedData = probObject.sort((a, b) => b.probability - a.probability);
// 정렬된 확률을 뒤집어서 새로운 객체 배열 생성
let reversedProbObject = sortedData.map((item, index) => ({
number: item.number,
probability: sortedData[sortedData.length - 1 - index].probability,
}));
let totalProbability = reversedProbObject.reduce(
(sum, item) => sum + item.probability,
0
);
while (numbers.length < 6) {
const ranNum = Math.random() * totalProbability;
let cumulativeProbability = 0;
for (let i = 0; i < reversedProbObject.length; i++) {
cumulativeProbability += reversedProbObject[i].probability;
if (ranNum <= cumulativeProbability) {
numbers.push(reversedProbObject[i].number);
totalProbability -= reversedProbObject[i].probability;
reversedProbObject.splice(i, 1);
break;
}
}
}
return numbers.sort((a, b) => a - b);
}
결론
여기에서는 객체에 확률들을 매핑해서 저장하고, 확률에 따라 객체를 내림차순으로 정렬한 후, 확률만 오름차순으로 뒤집어 새로운 객체 배열을 생성했다. 이후의 과정은 확률 기반 뽑기와 동일하지만, 누적 확률 초기화와 확률 중복 문제를 해결하기 위한 코드가 추가되었다.
이렇게 해서 모든 기능이 완료되었고, 각 뽑기 종류에 따라 한 번씩 클릭해 나온 숫자를 기념으로 동행복권 홈페이지에서 직접 구매해 보았다.
숫자는 아래 숫자들로 구매했다.
참고: 회차는 아직 크롤링 기능을 구현하지 않았으니 양해 바란다! 또 ChatGPT에 새로나온 Canvas 기능을 사용해 포스팅 해보았다 😊
'나만의 로또 번호 추첨 사이트' 카테고리의 다른 글
#4 : 확률을 반영한 로또 번호 추출 기능 만들기 (0) | 2024.10.06 |
---|---|
#3 : 랜덤 뽑기 기능 만들기 (Fisher-Yates 알고리즘) (5) | 2024.10.03 |
#2 : 기본적인 폴더 생성 및 메인 페이지 틀 제작 (2) | 2024.09.24 |
#1 : 생에 첫 프로젝트 : 로또 번호 랜덤 추출 사이트 (1) | 2024.09.19 |