/*
* PCG Random 알고리즘을 사용한 로또 번호 생성기
* 1~45 사이의 숫자에서 중복 없이 6개의 번호를 100세트 생성하고
* 각 세트 간의 중복 여부를 검사합니다.
*
* 아날로그 핀을 사용하여 무작위성을 높입니다.
*/
// PCG 난수 생성기 구현
class PCGRandom
{
private:
uint64_t state;
uint64_t inc;
public:
PCGRandom(uint64_t initstate = 0, uint64_t initseq = 0)
{
setState(initstate);
setIncrement(initseq);
}
void setState(uint64_t initstate)
{
state = 0;
next();
state += initstate;
next();
}
void setIncrement(uint64_t initseq)
{
inc = (initseq << 1) | 1;
}
uint32_t next()
{
uint64_t oldstate = state;
state = oldstate * 6364136223846793005ULL + inc;
uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
uint32_t rot = oldstate >> 59u;
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
}
uint32_t bounded(uint32_t bound)
{
uint32_t threshold = -bound % bound;
while (true)
{
uint32_t r = next();
if (r >= threshold)
return r % bound;
}
}
};
// 로또 번호 세트 (6개 숫자)를 표현하는 클래스
class LottoSet
{
public:
byte numbers[6];
void sort()
{
// 버블 정렬로 번호 정렬
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5 - i; j++)
{
if (numbers[j] > numbers[j + 1])
{
byte temp = numbers[j];
numbers[j] = numbers[j + 1];
numbers[j + 1] = temp;
}
}
}
}
// 두 로또 세트가 동일한지 비교
bool equals(const LottoSet &other) const
{
for (int i = 0; i < 6; i++)
{
if (numbers[i] != other.numbers[i])
{
return false;
}
}
return true;
}
// 출력용 문자열 변환
void toString(char *buffer)
{
sprintf(buffer, "%02d %02d %02d %02d %02d %02d",
numbers[0], numbers[1], numbers[2], numbers[3], numbers[4], numbers[5]);
}
};
// 로또 세트 저장소
const int MAX_SETS = 100;
LottoSet lottoSets[MAX_SETS];
int setsGenerated = 0;
// PCG 난수 생성기 인스턴스
PCGRandom pcg;
// 첫 번째 실행인지 확인하는 플래그
bool firstRun = true;
void setup()
{
Serial.begin(115200);
while (!Serial)
{
}
Serial.println(F("PCG Random 알고리즘을 이용한 로또 번호 생성기"));
Serial.println(F("1~45 사이의 숫자에서 중복 없이 6개의 번호를 100세트 생성합니다."));
Serial.println();
// 아날로그 핀에서 시드 값 읽기
randomSeed(analogRead(A0));
// PCG 초기화 - 아날로그 핀과 내부 타이머 값을 시드로 사용
uint64_t seed1 = (uint64_t)analogRead(A0) << 32 | analogRead(A1) << 16 | analogRead(A2);
uint64_t seed2 = (uint64_t)analogRead(A3) << 32 | analogRead(A4) << 16 | micros();
pcg = PCGRandom(seed1, seed2);
// 로또 번호 100세트 생성
generateLottoSets();
// 중복 검사 수행
checkDuplicates();
}
void loop()
{
// 루프에서는 추가 작업 없음
if (firstRun)
{
firstRun = false;
Serial.println(F("완료! 다시 실행하려면 리셋 버튼을 누르세요."));
}
}
// 로또 번호 세트 생성
void generateLottoSets()
{
Serial.println(F("로또 번호 생성 중..."));
for (int set = 0; set < MAX_SETS; set++)
{
generateLottoSet(&lottoSets[set]);
setsGenerated++;
// 10개 세트마다 진행 상황 출력
if ((set + 1) % 10 == 0 || set == 0)
{
Serial.print(F("생성된 세트: "));
Serial.println(set + 1);
}
}
Serial.println(F("로또 번호 생성 완료!"));
Serial.println();
}
// 단일 로또 세트 생성 (중복 숫자 없이)
void generateLottoSet(LottoSet *set)
{
bool used[46] = {false}; // 1-45 범위의 숫자 사용 여부 (0은 사용하지 않음)
for (int i = 0; i < 6; i++)
{
int num;
do
{
// 1-45 범위의 난수 생성
num = pcg.bounded(45) + 1;
} while (used[num]); // 이미 사용된 숫자라면 다시 생성
set->numbers[i] = num;
used[num] = true;
}
// 오름차순 정렬
set->sort();
}
// 모든 생성된 세트 간의 중복 검사
void checkDuplicates()
{
Serial.println(F("생성된 모든 로또 세트:"));
// 모든 세트 출력
char buffer[50];
for (int i = 0; i < setsGenerated; i++)
{
lottoSets[i].toString(buffer);
Serial.print(i + 1);
Serial.print(F(": "));
Serial.println(buffer);
}
Serial.println();
Serial.println(F("세트 간 중복 검사 중..."));
bool foundDuplicate = false;
int totalComparisons = 0;
// 모든 가능한 세트 쌍 비교
for (int i = 0; i < setsGenerated - 1; i++)
{
for (int j = i + 1; j < setsGenerated; j++)
{
totalComparisons++;
if (lottoSets[i].equals(lottoSets[j]))
{
foundDuplicate = true;
Serial.print(F("중복 발견! 세트 #"));
Serial.print(i + 1);
Serial.print(F(" 와 세트 #"));
Serial.println(j + 1);
char buffer1[50], buffer2[50];
lottoSets[i].toString(buffer1);
lottoSets[j].toString(buffer2);
Serial.print(F(" 세트 #"));
Serial.print(i + 1);
Serial.print(F(": "));
Serial.println(buffer1);
Serial.print(F(" 세트 #"));
Serial.print(j + 1);
Serial.print(F(": "));
Serial.println(buffer2);
}
}
// 진행 상황 표시 (10% 단위)
if ((i + 1) % (setsGenerated / 10) == 0)
{
int percent = ((i + 1) * 100) / (setsGenerated - 1);
Serial.print(F("검사 진행: "));
Serial.print(percent);
Serial.println(F("%"));
}
}
Serial.println();
Serial.print(F("총 비교 횟수: "));
Serial.println(totalComparisons);
if (foundDuplicate)
{
Serial.println(F("중복된 세트가 발견되었습니다!"));
}
else
{
Serial.println(F("중복된 세트가 없습니다! 모든 100개 세트는 고유합니다."));
}
// 통계 검사 추가 - 세 가지 체크 로직
checkStatistics();
}
// 생성된 로또 번호의 통계 검사 (세 가지 체크 로직)
void checkStatistics()
{
Serial.println();
Serial.println(F("===== 로또 번호 통계 검사 ====="));
// 1. 체크 로직 1: 각 숫자의 출현 빈도
Serial.println(F("1. 각 숫자의 출현 빈도:"));
int numberFrequency[46] = {0}; // 1-45의 숫자 빈도
for (int i = 0; i < setsGenerated; i++)
{
for (int j = 0; j < 6; j++)
{
numberFrequency[lottoSets[i].numbers[j]]++;
}
}
// 출현 빈도 출력
for (int i = 1; i <= 45; i++)
{
Serial.print(F("숫자 "));
Serial.print(i);
Serial.print(F(": "));
Serial.print(numberFrequency[i]);
Serial.print(F("회"));
if (i % 5 == 0)
{
Serial.println();
}
else
{
Serial.print(F("\t"));
}
}
Serial.println();
// 2. 체크 로직 2: 홀수/짝수 분포
Serial.println(F("2. 홀수/짝수 분포:"));
int oddEvenDistribution[7] = {0}; // 홀수 0개~6개 분포
for (int i = 0; i < setsGenerated; i++)
{
int oddCount = 0;
for (int j = 0; j < 6; j++)
{
if (lottoSets[i].numbers[j] % 2 == 1)
{ // 홀수 카운트
oddCount++;
}
}
oddEvenDistribution[oddCount]++;
}
Serial.println(F("홀수 개수별 세트 수:"));
for (int i = 0; i <= 6; i++)
{
Serial.print(F("홀수 "));
Serial.print(i);
Serial.print(F("개 (짝수 "));
Serial.print(6 - i);
Serial.print(F("개): "));
Serial.print(oddEvenDistribution[i]);
Serial.println(F("세트"));
}
// 3. 체크 로직 3: 총합 분포
Serial.println(F("3. 번호 총합 분포:"));
int sumDistribution[300] = {0}; // 가능한 총합 범위
int minSum = 255, maxSum = 0;
for (int i = 0; i < setsGenerated; i++)
{
int sum = 0;
for (int j = 0; j < 6; j++)
{
sum += lottoSets[i].numbers[j];
}
sumDistribution[sum]++;
if (sum < minSum)
minSum = sum;
if (sum > maxSum)
maxSum = sum;
}
Serial.print(F("최소 총합: "));
Serial.println(minSum);
Serial.print(F("최대 총합: "));
Serial.println(maxSum);
Serial.println(F("총합 분포:"));
for (int i = minSum; i <= maxSum; i++)
{
if (sumDistribution[i] > 0)
{
Serial.print(F("총합 "));
Serial.print(i);
Serial.print(F(": "));
Serial.print(sumDistribution[i]);
Serial.println(F("세트"));
}
}
}