lotto005.cpp

#include <stdio.h>      /* printf, scanf, puts, NULL */

#include <stdlib.h>     /* srand, rand */

#include <time.h>       /* time */

#include <iostream> /* cout, endl */

#include <algorithm>    // std::sort, std::find, std::copy

#include <vector>       // std::vector


using namespace std;


// 로또조합 순서값(1~8145060)으로 로또조합 알아내기

void getLottoNumberByOrderNumber(int o, int *r)

{

int sum=0,nn=0,a=0,b=0,c=0,d=0,e=0,f=0;


for(a=1; a<=40; a++) {

nn = ((45-a)*(45-a-1)*(45-a-2)*(45-a-3)*(45-a-4)) / (5*4*3*2*1);

if(sum+nn>=o) break;

sum += nn;

}



for(b=a+1; b<=41; b++) {

nn = ((45-b)*(45-b-1)*(45-b-2)*(45-b-3)) / (4*3*2*1);

if(sum+nn>=o) break;

sum += nn;

}


for(c=b+1; c<=42; c++) {

nn = ((45-c)*(45-c-1)*(45-c-2)) / (3*2*1);

if(sum+nn>=o) break;

sum += nn;

}


for(d=c+1; d<=43; d++) {

nn = ((45-d)*(45-d-1)) / (2*1);

if(sum+nn>=o) break;

sum += nn;

}


for(e=d+1; e<=44; e++) {

nn = ((45-e)) / (1);

if(sum+nn>=o) break;

sum += nn;

}


for(f=e+1; f<=45; f++) {

nn = 1;

if(sum+nn>=o) break;

sum += nn;

}


sum++;

r[0] = a;

r[1] = b;

r[2] = c;

r[3] = d;

r[4] = e;

r[5] = f;

}


// 로또 조합이 8145060 조합중 몇번째 조합인지 알아내기

int getOrderNumber(int r[])

{

int sum=0,nn=0,a=0,b=0,c=0,d=0,e=0,f=0;


for(a=1; a<r[0]; a++) {

nn = ((45-a)*(45-a-1)*(45-a-2)*(45-a-3)*(45-a-4)) / (5*4*3*2*1);

sum += nn;

}


for(b=a+1; b<r[1]; b++) {

nn = ((45-b)*(45-b-1)*(45-b-2)*(45-b-3)) / (4*3*2*1);

sum += nn;

}


for(c=b+1; c<r[2]; c++) {

nn = ((45-c)*(45-c-1)*(45-c-2)) / (3*2*1);

sum += nn;

}


for(d=c+1; d<r[3]; d++) {

nn = ((45-d)*(45-d-1)) / (2*1);

sum += nn;

}


for(e=d+1; e<r[4]; e++) {

nn = ((45-e)) / (1);

sum += nn;

}


for(f=e+1; f<r[5]; f++) {

nn = 1;

sum += nn;

}

sum++;

return sum;

}


// 1등 당첨번호+보너스번호를 이용하며 모든 1~5등 조합 생성하기

void make1to5ByWinLotto(int *r, std::vector<int> *v) {


v->clear(); // 결과를 저장할 벡터 변수 clear

for(int i=0; i<7; i++) {

if(r[i]<1 || 45<r[i]) return; // 1등 로또번호+보너스볼의 무결성 체크 #1

if(i==6) break;

for(int j=i+1; j<7; j++) {

if(r[i]==r[j]) return; // 1등 로또번호+보너스볼의 무결성 체크 #2

}

}


int count=0; 


int y[6] = {0};

std::vector<int> vv;



// 1등 1개

memcpy(y, r, 6*sizeof(int));

vv.clear();

vv.assign(y, y+6); // 1등 로또 번호에서 6수만 vv 벡터로 복사 

std::sort(vv.begin(), vv.end()); // 정렬

std::copy(vv.begin(), vv.end(), y); // 복사 

int ordernumber = getOrderNumber(y);

v->push_back(ordernumber);

count++;

// for(int i=0; i<vv.size(); i++)

// cout << " " << vv[i];

// cout << endl;


cout << "count = " << count << endl; // 1 = 1



// 2등 6개

for(int i=0; i<6; i++) {

memcpy(y, r, 6*sizeof(int));

y[i] = r[6]; // 보너스볼

vv.clear();

vv.assign(y, y+6); // 1등 로또 번호에서 6수만 vv 벡터로 복사 

std::sort(vv.begin(), vv.end()); // 정렬

std::copy(vv.begin(), vv.end(), y); // 복사 

int ordernumber = getOrderNumber(y);

v->push_back(ordernumber);

count++;

// for(int i=0; i<vv.size(); i++)

// cout << " " << vv[i];

// cout << endl;

}

cout << "count = " << count << endl; // 1 + 6 = 7



//  3등 228개 ( 6 * (45-7) = 6*38 = 228 )

for(int i=0; i<6; i++) {

for(int j=1; j<=45; j++) {

if(r[0]==j || r[1]==j || r[2]==j || r[3]==j || r[4]==j || r[5]==j || r[6]==j) continue;

memcpy(y, r, 6*sizeof(int));

y[i] = j; // 당첨번호,보너스번호가 아닌 수를 입력 

vv.clear();

vv.assign(y, y+6); // 1등 로또 번호에서 6수만 vv 벡터로 복사 

std::sort(vv.begin(), vv.end()); // 정렬

std::copy(vv.begin(), vv.end(), y); // 복사 

int ordernumber = getOrderNumber(y);

v->push_back(ordernumber);

count++;

}

}

cout << "count = " << count << endl; // 1 + 6 + 228 = 235



// 4등 11,115개 // 6수중2수를 빼고 + 39수중2수를 뽑는다.

for(int i=0; i<5; i++) {

for(int j=i+1; j<6; j++) { // 6수중 2수를 제거


for(int k=1; k<=44; k++) {

if(r[0]==k || r[1]==k || r[2]==k || r[3]==k || r[4]==k || r[5]==k) continue;

for(int l=k+1; l<=45; l++) {

if(r[0]==l || r[1]==l || r[2]==l || r[3]==l || r[4]==l || r[5]==l) continue;

memcpy(y, r, 6*sizeof(int));

y[i] = k;

y[j] = l;

vv.clear();

vv.assign(y, y+6); // 1등 로또 번호에서 6수만 vv 벡터로 복사 

std::sort(vv.begin(), vv.end()); // 정렬

std::copy(vv.begin(), vv.end(), y); // 복사 

int ordernumber = getOrderNumber(y);

v->push_back(ordernumber);

count++;

}

}

}

}

cout << "count = " << count << endl; // 1 + 6 + 228 + 11115 = 11350


// 5등 182,780개 // 6수중3수를 빼고 + 39수중3수를 뽑는다.

for(int i=0; i<4; i++) {

for(int j=i+1; j<5; j++) {

for(int k=j+1; k<6; k++) { // 6수중 3수를 제거


for(int l=1; l<=43; l++) {

if(r[0]==l || r[1]==l || r[2]==l || r[3]==l || r[4]==l || r[5]==l) continue;

for(int m=l+1; m<=44; m++) {

if(r[0]==m || r[1]==m || r[2]==m || r[3]==m || r[4]==m || r[5]==m) continue;

for(int n=m+1; n<=45; n++) {

if(r[0]==n || r[1]==n || r[2]==n || r[3]==n || r[4]==n || r[5]==n) continue;

memcpy(y, r, 6*sizeof(int));

y[i] = l;

y[j] = m;

y[k] = n;

vv.clear();

vv.assign(y, y+6); // 1등 로또 번호에서 6수만 vv 벡터로 복사 

std::sort(vv.begin(), vv.end()); // 정렬

std::copy(vv.begin(), vv.end(), y); // 복사 

int ordernumber = getOrderNumber(y);

v->push_back(ordernumber);

count++;

}

}

}

}

}

}

cout << "count = " << count << endl; // 1 + 6 + 228 + 11115 + 182780 = 194130



std::sort(v->begin(), v->end()); // 정렬

vector<int>::iterator last = std::unique(v->begin(), v->end()); // 중복제거

v->erase(last, v->end()); // 중복제거후 완전히 삭제되지 않은 것들 삭제

//v->erase(std::unique(v->begin(), v->end()), v->end()); // 중복제거 + 찌꺼기삭제 통합


}


int main(int argc, char *argv[])

{

int r[7] = {1,2,3,4,5,6,7}; // 1등 당첨번호 + 보너스번호 

std::vector<int> v; // 1등 당첨번호를 기준으로 1~5등에 해당하는 모든 조합을 저장할 벡터 변수 


make1to5ByWinLotto(r, &v);

// r=당첨번호+보너스볼=7수 를 이용하여,

// 모든 당첨번호(1~5등)의 orderNumber 를 v 벡터에 저장한다.

// (1등+2등+3등+4등+5등 = 1+6+228+11115+182780 = 194,130)


printf("\n");

printf("%02d,%02d,%02d,%02d,%02d,%02d / %02d\n", r[0],r[1],r[2],r[3],r[4],r[5],r[6]); // 1등 당첨번호+보너스번호 출력 

printf("\n");

int r2[6]={0,}; // 로또번호를 저장할 배열 

for(int i=0; i<v.size(); i++) {

getLottoNumberByOrderNumber(v[i], r2);

printf("%02d,%02d,%02d,%02d,%02d,%02d\n", r2[0],r2[1],r2[2],r2[3],r2[4],r2[5]);

// 1~5등에 해당하는 모든 조합 출력 (194,130개) 

}

printf("\n");


getchar();

return 0;

}


1등 당첨번호+보너스번호를 이용하며 모든 1~5등 조합을 생성하는 소스코드 입니다.


로또 1등 당첨번호+보너스번호를 기준으로 8145060 로또조합 중

1등은 1개

2등은 6개

3등은 228개

4등은 11,115개

5등은 182,780개 가 존재하게 됩니다.






Posted by 잇힝2012
,

6수로 이루어진 로또번호를 랜덤하게 출력 합니다.

단순히 1~45 사이의 수 6개를 랜덤하게 생성하는 방법은 이전 포스팅에서 해 보았고 (http://sociallottowork.tistory.com/35)

여기서는 1~8145060 사이의 수 1개를 랜덤 생성한 후, 이 번호를 로또번호로 변환하는 방법을 사용합니다.


1~8145060 이라는 큰수 랜덤을 효과적으로 생성하기 위해 WELL Random (http://sociallottowork.tistory.com/36) 을 사용하고,

이렇게 생성된 번호를 로또번호로 변환하기 위해 getLottoNumberByOrderNumber 함수 (http://sociallottowork.tistory.com/33) 를 사용할 것입니다.


이 방법을 사용하면, 로또 시뮬레이터를 구현할때, 랜덤 생성 과정을 현격히 줄여, 고속 시뮬레이터 구현이 가능하게 됩니다.

또한 여러 로또 조합에 대한 정렬 및 차집합 계산 등에 있어서도 유리합니다.

즉, 로또프로그램 내부에서 로또번호조합이 아닌, 정수(1~8145060)로만 취급하게 됨으로써 연산에 있어 여러가지 이점을 얻을 수 있습니다.


아래는 그 내용을 담고 있는 소스코드 입니다.


WELL1024a.cpp

/* ***************************************************************************** */

/* Copyright:      Francois Panneton and Pierre L'Ecuyer, University of Montreal */

/*                 Makoto Matsumoto, Hiroshima University                        */

/* Notice:         This code can be used freely for personal, academic,          */

/*                 or non-commercial purposes. For commercial purposes,          */

/*                 please contact P. L'Ecuyer at: lecuyer@iro.UMontreal.ca       */

/* ***************************************************************************** */


#define W 32

#define R 32

#define M1 3

#define M2 24

#define M3 10


#define MAT0POS(t,v) (v^(v>>t))

#define MAT0NEG(t,v) (v^(v<<(-(t))))

#define Identity(v) (v)


#define V0            STATE[state_i                   ]

#define VM1           STATE[(state_i+M1) & 0x0000001fU]

#define VM2           STATE[(state_i+M2) & 0x0000001fU]

#define VM3           STATE[(state_i+M3) & 0x0000001fU]

#define VRm1          STATE[(state_i+31) & 0x0000001fU]

#define newV0         STATE[(state_i+31) & 0x0000001fU]

#define newV1         STATE[state_i                   ]


#define FACT 2.32830643653869628906e-10


static unsigned int state_i = 0;

static unsigned int STATE[R];

static unsigned int z0, z1, z2;


void InitWELLRNG1024a (unsigned int *init){

   int j;

   state_i = 0;

   for (j = 0; j < R; j++)

     STATE[j] = init[j];

}


double WELLRNG1024a (void){

  z0    = VRm1;

  z1    = Identity(V0)       ^ MAT0POS (8, VM1);

  z2    = MAT0NEG (-19, VM2) ^ MAT0NEG(-14,VM3);

  newV1 = z1                 ^ z2; 

  newV0 = MAT0NEG (-11,z0)   ^ MAT0NEG(-7,z1)    ^ MAT0NEG(-13,z2) ;

  state_i = (state_i + 31) & 0x0000001fU;

  return ((double) STATE[state_i]  * FACT);

}


WELL1024a.h

/* ***************************************************************************** */

/* Copyright:      Francois Panneton and Pierre L'Ecuyer, University of Montreal */

/*                 Makoto Matsumoto, Hiroshima University                        */

/* Notice:         This code can be used freely for personal, academic,          */

/*                 or non-commercial purposes. For commercial purposes,          */

/*                 please contact P. L'Ecuyer at: lecuyer@iro.UMontreal.ca       */

/* ***************************************************************************** */


void InitWELLRNG1024a (unsigned int *init);

double WELLRNG1024a (void);


main.cpp

#include <stdio.h>      /* printf, scanf, puts, NULL */

#include <stdlib.h>     /* srand, rand */

#include <iostream> /* cout, endl */

#include <time.h>       /* time */

#include "WELL1024a.h" // WELL Random number generator  http://www.iro.umontreal.ca/~panneton/WELLRNG.html


using namespace std;


// 로또조합 순서값(1~8145060)으로 로또조합 알아내기

void getLottoNumberByOrderNumber(int o, int *r)

{

int sum=0,nn=0,a=0,b=0,c=0,d=0,e=0,f=0;


for(a=1; a<=40; a++) {

nn = ((45-a)*(45-a-1)*(45-a-2)*(45-a-3)*(45-a-4)) / (5*4*3*2*1);

if(sum+nn>=o) break;

sum += nn;

}


for(b=a+1; b<=41; b++) {

nn = ((45-b)*(45-b-1)*(45-b-2)*(45-b-3)) / (4*3*2*1);

if(sum+nn>=o) break;

sum += nn;

}


for(c=b+1; c<=42; c++) {

nn = ((45-c)*(45-c-1)*(45-c-2)) / (3*2*1);

if(sum+nn>=o) break;

sum += nn;

}


for(d=c+1; d<=43; d++) {

nn = ((45-d)*(45-d-1)) / (2*1);

if(sum+nn>=o) break;

sum += nn;

}


for(e=d+1; e<=44; e++) {

nn = ((45-e)) / (1);

if(sum+nn>=o) break;

sum += nn;

}


for(f=e+1; f<=45; f++) {

nn = 1;

if(sum+nn>=o) break;

sum += nn;

}


sum++;

r[0] = a;

r[1] = b;

r[2] = c;

r[3] = d;

r[4] = e;

r[5] = f;

}


int main(int argc, char *argv[])

{

srand((unsigned)time(NULL));

unsigned int init[32];

for(int i=0; i<32; i++) {

init[i] = rand()<<16 | rand();

// WELL Random 을 초기화 하기 위해, C 표준 rand() 함수를 이용하여 init 값을 생성합니다

}

InitWELLRNG1024a(init); // WELL Random 초기화

for(int i=0; i<10; i++) {

int rnd = (int)((double)WELLRNG1024a() * (8145060-1+1)) + 1; // 1~8145060 정수 랜덤 생성

int r1[6]={0,}; // 로또번호를 저장할 배열

getLottoNumberByOrderNumber(rnd, r1); // 로또 순서 rnd 번에 해당하는 로또번호를 r1 배열에 저장


printf("%02d,%02d,%02d,%02d,%02d,%02d\n", r1[0],r1[1],r1[2],r1[3],r1[4],r1[5]);

}

printf("\n");


getchar();

return 0;

}


위 소스코드를 응용하면 로또 고속시뮬레이터등을 구현 할 수 있습니다.


소셜로또워크(http://www.sociallottowork.com/bbs/board.php?bo_table=download) 의 시뮬레이터와

소셜로또워크 시뮬레이터 안드로이드 앱(http://www.sociallottowork.com/bbs/board.php?bo_table=download&wr_id=68) 이

위의 방법을 사용합니다.


Posted by 잇힝2012
,


C/C++ 에서 rand() 의 기본 사용법은 다음과 같습니다.


random0.cpp

#include <stdio.h>      /* printf, scanf, puts, NULL */

#include <stdlib.h>     /* srand, rand */

#include <time.h>       /* time */


int main(int argc, char *argv[])

{

srand((unsigned)time(NULL)); // 랜덤 씨드 초기화


printf("RAND_MAX = %d\n\n", RAND_MAX); // 32767 = 0x7FFFF = 0111 1111 1111 1111


for(int i=0; i<10; i++)

printf("%d\n", rand()); // 0~32767 랜덤 발생 (0~RAND_MAX) 

printf("\n");

for(int i=0; i<10; i++)

printf("%d\n", rand()%10); // 0~9 랜덤 발생

printf("\n");


for(int i=0; i<10; i++)

printf("%d\n", rand()%10 + 5); // 5~14 랜덤 발생

printf("\n");

getchar();

return 0;

}


하지만 C 표준 rand() 함수는 생성 범위가 제한적이기 때문에 큰수 랜덤 생성에 적합하지 않습니다.

이를 해결하기 위해 많은 방법들이 있지만,

여기서는 WELL Random number generator 를 C/C++ 에서 사용하는 방법에 대해 알아보겠습니다.


(WELL Random 은 메르센 트위스터를 만든 사람이 개선한 것이랍니다)

메르센 트위스터

위키백과, 우리 모두의 백과사전.

메르센 트위스터(Mersenne Twister)는 1997년에 마츠모토 마코토(松本 眞)와 니시무라 다쿠지(西村 拓士)가 개발한 유사난수 생성기이다.[1] 메르센 트위스터는 동일한 저자들이 개발한 TT800 생성기의 개선판으로, 기존 생성기들의 문제점들을 피하면서 매우 질이 좋은 난수를 빠르게 생성할 수 있도록 설계되었다.

메르센 트위스터의 이름은 난수의 반복 주기가 메르센 소수인 데에서 유래했다. 메르센 트위스터는 그 속도와 난수의 품질 때문에 점점 많은 곳에서 채택되고 있으며, 흔히 주기가 2^{19937}-1인 MT19937을 사용한다. MT19937과 같으나 생성해 내는 난수가 32비트가 아닌 64비트인 MT19937-64도 쓰이며, 2006년에 동일한 저자들이 발표한 SIMD 기반 메르센 트위스터는 MT19937에 비해 대략 두 배 정도 빠른 것으로 알려져 있다.

난수의 품질에도 불구하고, 메르센 트위스터는 암호학적으로 안전한 유사난수 생성기가 아니다. 즉 난수의 특성(주기, 난수 범위)을 알고 있을 때 유한한 수의 난수(이 경우 624개)만으로 현재 생성기의 상태를 알아 낼 수 있으며, 그 뒤에 나올 난수를 예측해 낼 수 있다. 암호학적으로 안전한 유사난수 생성기를 얻기 위해서는 해시 함수를 사용해야 하지만 난수의 생성 속도가 낮아진다. 또는 블룸 블룸 슙(BBS)과 같이 암호학적으로 안전하게 설계된 생성기를 쓸 수도 있다.


출처: http://ko.wikipedia.org/wiki/메르센_트위스터


WELL Random number generator 소스는 http://www.iro.umontreal.ca/~panneton/WELLRNG.html 에서 구할 수 있습니다.

512, 1024, 19937, 44497 의 4종류가 보이는데, 적당히 1024 를 사용하기로 합니다.


WELL1024a.cpp

/* ***************************************************************************** */

/* Copyright:      Francois Panneton and Pierre L'Ecuyer, University of Montreal */

/*                 Makoto Matsumoto, Hiroshima University                        */

/* Notice:         This code can be used freely for personal, academic,          */

/*                 or non-commercial purposes. For commercial purposes,          */

/*                 please contact P. L'Ecuyer at: lecuyer@iro.UMontreal.ca       */

/* ***************************************************************************** */


#define W 32

#define R 32

#define M1 3

#define M2 24

#define M3 10


#define MAT0POS(t,v) (v^(v>>t))

#define MAT0NEG(t,v) (v^(v<<(-(t))))

#define Identity(v) (v)


#define V0            STATE[state_i                   ]

#define VM1           STATE[(state_i+M1) & 0x0000001fU]

#define VM2           STATE[(state_i+M2) & 0x0000001fU]

#define VM3           STATE[(state_i+M3) & 0x0000001fU]

#define VRm1          STATE[(state_i+31) & 0x0000001fU]

#define newV0         STATE[(state_i+31) & 0x0000001fU]

#define newV1         STATE[state_i                   ]


#define FACT 2.32830643653869628906e-10


static unsigned int state_i = 0;

static unsigned int STATE[R];

static unsigned int z0, z1, z2;


void InitWELLRNG1024a (unsigned int *init){

   int j;

   state_i = 0;

   for (j = 0; j < R; j++)

     STATE[j] = init[j];

}


double WELLRNG1024a (void){

  z0    = VRm1;

  z1    = Identity(V0)       ^ MAT0POS (8, VM1);

  z2    = MAT0NEG (-19, VM2) ^ MAT0NEG(-14,VM3);

  newV1 = z1                 ^ z2; 

  newV0 = MAT0NEG (-11,z0)   ^ MAT0NEG(-7,z1)    ^ MAT0NEG(-13,z2) ;

  state_i = (state_i + 31) & 0x0000001fU;

  return ((double) STATE[state_i]  * FACT);

}


WELL1024a.h

/* ***************************************************************************** */

/* Copyright:      Francois Panneton and Pierre L'Ecuyer, University of Montreal */

/*                 Makoto Matsumoto, Hiroshima University                        */

/* Notice:         This code can be used freely for personal, academic,          */

/*                 or non-commercial purposes. For commercial purposes,          */

/*                 please contact P. L'Ecuyer at: lecuyer@iro.UMontreal.ca       */

/* ***************************************************************************** */


void InitWELLRNG1024a (unsigned int *init);

double WELLRNG1024a (void);


main.cpp

#include <stdio.h>      /* printf, scanf, puts, NULL */

#include <stdlib.h>     /* srand, rand */

#include <iostream> /* cout, endl */

#include <time.h>       /* time */

#include "WELL1024a.h" // WELL Random number generator  http://www.iro.umontreal.ca/~panneton/WELLRNG.html



using namespace std;


int main(int argc, char *argv[])

{

srand((unsigned)time(NULL));

unsigned int init[32];

for(int i=0; i<32; i++) {

init[i] = rand()<<16 | rand();

// WELL Random 을 초기화 하기 위해, C 표준 rand() 함수를 이용하여 init 값을 생성합니다

}

InitWELLRNG1024a(init); // WELL Random 초기화

// 기본 사용법 

// double x = (double)WELLRNG1024a(); // 0.0 <= x < 1.0  실수 랜덤 생성


for(int i=0; i<10; i++)

printf("%f\n", (double)WELLRNG1024a()); // 0.0 <= x < 1.0  실수 랜덤 생성

printf("\n");

for(int i=0; i<10; i++)

printf("%d\n", (int)((double)WELLRNG1024a() * 1000001) ); // 0~1,000,000 정수 랜덤 생성

printf("\n");


for(int i=0; i<10; i++)

printf("%d\n", (int)((double)WELLRNG1024a() * 1000001) + 5 ); // 5~1,000,005 정수 랜덤 생성

printf("\n");


getchar();

return 0;

}


기본적인 사용법은

InitWELLRNG1024a(init); 으로 최초 1회 초기화 해준 후,

(double)WELLRNG1024a(); 로 랜덤값(실수, double, 0.0 <= x < 1.0 )을 가져오면 됩니다.


리턴값이 실수(double)이기 때문에, 특정 범위의 정수 값으로 변환하기 위해선 아래와 같은 방법을 사용합니다.

int rnd = (int)((double)WELLRNG1024a() * (max-min+1)) + min; // min~max 정수 랜덤 생성


(틀린 내용이 있다면 알려주세요)


Posted by 잇힝2012
,