여러분께서는 중복되지 않는 숫자를 어떻게 생성하시나요?

espoirgod의 이미지

void *gen_randnum(char *g_num)
{
	short int d_check = 0;	// 중복된 숫자를 검사하는데 사용되는 변수
	int cipher = 0;			// 자리수를 나타내는 변수(즉, N에 곱해지는 수)
	int temp;				// 임시 변수

	puts("\n--------------------------");
	puts("임의의 숫자를 생성중입니다");
	puts("--------------------------");

	srand((unsigned)time(NULL));			// Seed 값 초기화
	
	while (cipher < DIGIT_LEN) {
		temp = rand() % 10;

		// 생성된 숫자가 중복되었다면 다시 생성
		if ((d_check >> temp) & 1)
			continue;

		// 생성된 숫자가 중복되지 않았다면 다음의 명령을 실행
		else {
			g_num[cipher++] = temp;
			d_check |= (1 << temp);
		}
	}

	puts("\n---------------------");
	puts("숫자가 생성되었습니다");
	puts("---------------------\n");

	return g_num;	// 생성된 숫자의 시작 주소를 return
}

아래는 이 코드에 대한 설명이예요

/************************************************************************/
/* 원  형: void gen_randnum(char *g_num)                                */
/* 리턴값: 중복되지 않는 4자리 양의 정수로 구성된 수                    */
/* 설  명: 무작위로 생성된 0 ~ 9 까지의 숫자를 N 이라 할 때,            */
/*         처음에는 N, 그 다음에는 10N, 100N, 마지막으로 1000N으로      */
/*         처리하여서 임의의 4자리 정수를 생성한다.                     */
/*         중복된 숫자는 LSB부터 9번째 까지의 비트의 각 값을 on/off     */
/*         시켜서 구분을 한다. 그 위치의 비트가 1(on)이면 그 숫자는     */
/*         생성되었던 숫자임을 의미한다. 0(off)이면 그 숫자는 처음      */
/*         생성된 숫자임을 의미한다.                                    */
/*																		*/
/*           9   8   7   6   5   4   3   2   1   0						*/
/*		   ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐					*/
/*         │0 │0 │0 │0 │0 │0 │0 │0 │0 │0 │					*/
/*         └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘					*/
/*          이 그림은 0 ~ 9 까지의 숫자중 어떤 숫자도 생성된 적이       */
/*          없음을 의미한다.                                            */
/*																		*/
/*          만약 처음에 생성된 숫자가 9 라면 위의 표는					*/
/*	 	    아래와 같이 변경된다.										*/
/*																		*/
/*           9   8   7   6   5   4   3   2   1   0						*/
/*		   ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐					*/
/*         │1 │0 │0 │0 │0 │0 │0 │0 │0 │0 │					*/
/*         └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘					*/
/*          이 그림은 0 ~ 9 까지의 숫자중 9 만 생성된 적이 있음을  		*/
/*          의미한다.													*/
/*																		*/
/************************************************************************/

여러분께서는 어떻게 하시나요?
좀 더 효율적인 방법(속도)이 있으시다면 알려주세요^0^

happyjun의 이미지

0~9의 숫자를 임의로 섞은 후 필요한 만큼 뽑으면 될 듯 합니다.

----------------------------------------
http://moim.at
http://mkhq.co.kr

j0nguk의 이미지

효율은 잘 모르겠구요.

말씀대로 어떤 것을 뽑았는지 표시할 수도 있지만, 한 번 뽑은 것을 배열의 가장 끝과 바꾸는 방법도 있죠.

1. 범위가 1~N이라면 N개의 배열을 잡고,
2. 차례로 숫자 넣어둔 뒤에,
3. 임의로 뽑고
4. 뽑은 수는 가장 뒤에 수(index N에 위치한)와 바꾸고,

다시 3으로 가서 뽑되, N-1의 범위에서만 뽑는거죠. 한 번 뽑은 수는 무시하고요. 혹시 연속으로 같은 index를 집더라도 다른 수를 얻겠죠. 마지막 index에 있었던 녀석과 바꾸었으니까요.

espoirgod의 이미지

Quote:
1. 범위가 1~N이라면 N개의 배열을 잡고,
2. 차례로 숫자 넣어둔 뒤에,
3. 임의로 뽑고
4. 뽑은 수는 가장 뒤에 수(index N에 위치한)와 바꾸고,

다시 3으로 가서 뽑되, N-1의 범위에서만 뽑는거죠. 한 번 뽑은 수는 무시하고요. 혹시 연속으로 같은 index를 집더라도 다른 수를 얻겠죠. 마지막 index에 있었던 녀석과 바꾸었으니까요.

그렇다면.. 1 - 1000 까지의 숫자(즉, N 은 1000)를 발생시킨다면
1000 * 4 bytes = 4000 bytes 가 필요하고..;;
생성된 수의 개수가 M 이라고 할 때,
선택되는 index 는 0 ~ (N - M - 1) 이 겠네요. (배열의 인덱스가 0 부터이므로..)
물론, 4번의 연산도 수행하구요~

감사합니다^-^

Quote:
0~9의 숫자를 임의로 섞은 후 필요한 만큼 뽑으면 될 듯 합니다.

happyjun 님, 좀 더 자세히 설명해 주실 수 있나요?
너무 간단해서 정확히 잘 모르겠습니다;;
임의의 숫자를 생성하기 위해서 거치는
각 단계를 명확히 정의해 줄 수 있나요?

배움의 목적을 잊지 말자!!

lifthrasiir의 이미지

espoirgod wrote:
Quote:
0~9의 숫자를 임의로 섞은 후 필요한 만큼 뽑으면 될 듯 합니다.

happyjun 님, 좀 더 자세히 설명해 주실 수 있나요?
너무 간단해서 정확히 잘 모르겠습니다;;
임의의 숫자를 생성하기 위해서 거치는
각 단계를 명확히 정의해 줄 수 있나요?

이 말이 위에 있는 알고리즘과 본질적으로 같은 의미입니다. 즉, 1부터 N까지의 숫자 중 겹치지 않는 것을 뽑는다면 1부터 N까지의 숫자가 차 있는 배열을 뒤섞은 다음에 순서대로 뽑는데, j0nguk 님께서 소개하신 것은 뒤섞으면서 바로 바로 뽑는 방법이 되겠지요.

위와 같이 O(N)의 시간 복잡도로 배열을 뒤섞는 방법을 Knuth shuffle이라고 합니다. Donald Knuth의 이름이 여기서도 등장하는 군요.

- 토끼군

espoirgod의 이미지

tokigun 님 좋은 정보 감사합니다^-^

제가 올렸던 건 N(N <= 10)자리의 숫자를 생성하는데(N은 자연수)
각 자리의 수에 어떤 수도 중복되지 않는 수를 생성하는 거예요.

6012345 는 되지만 975383 은 안되요.

제가 맨 처음에 올렸던 거랑은 약간의 차이가 있는게 맞죠?
어쨋든 몰랐던 알고리즘인데 알려주셔서 감사합니다.

배움의 목적을 잊지 말자!!

wpcasper의 이미지

중복된 수가 있는지 검사하는 루틴이 들어간다면 효율이 굉장히 떨어지게 됩니다. 애초에 중복해서 뽑지 않는것이 가장 바람직하죠. 극단적인 확률로 영원히 수를 뽑아내지 못할수도 있습니다. 0에 가까운 확률이지만요. 무조건 뽑을수 있다와 확률적으로 뽑을수 있다의 차이랄까요?

처음에 올리신 방법으로 한번만에 4자리 정수를 뽑아낼 확률은 0.5입니다. 한번만에 뽑아낸다고 해도 다른 알고리즘보다 4번의 비교연산이 들어가기 때문에 느립니다. 물론, 간단한 프로그램이라면 체감할정도는 아닙니다. 토끼군님께서 올리신건 처음 올리신것과 같은 문제를 해결하는 방법입니다. (0~9에서 중복하지 않고 무작위로 4개를 뽑아낸 순열, N=10일때의 특정한경우)

저... 죄송합니다만. 섞은다음에 필요한만큼 뽑아내는것에서 섞는건 어떻게 구현하나요?? 알고리즘 자체는 굉장히 쉽게 이해되는데.. 의사코드로 어찌 구현해야할지 모르겠네요.

M.W.Park의 이미지

옛날 생각이 나네요.
카드를 섞어야하는데, 52장을 섞는데...
난수를 발생시킨다음에 이미 뽑은 것과 비교를 하는 후배녀석의 코드를 보고, "최악의 경우는 루프가 끝나지 않을 수 있다"고 말했더니, 그녀석 왈 "아직 한번도 그런 경우는 없었는데요."라고... 8)
근본적으로 언제 끝날지를 예측하기 힘든 루프는 재작성하는게 바람직하다고 생각합니다.

wpcasper wrote:

저... 죄송합니다만. 섞은다음에 필요한만큼 뽑아내는것에서 섞는건 어떻게 구현하나요?? 알고리즘 자체는 굉장히 쉽게 이해되는데.. 의사코드로 어찌 구현해야할지 모르겠네요.

난수를 충분히 생성한다음 두개씩 묶어서 자리를 바꾸는것이 젤 간단하더군요.

-----
오늘 의 취미는 끝없는, 끝없는 인내다. 1973 法頂

Fe.head의 이미지

10개의 숫자를 가질수 있는 배열에 0 ~ 9를 넣는다.
임의의 개수만큼 섞는다.(아래 반복)
	0~9중 임의의 수 2개를 구한다.
	구한 두 숫자에 해당하는 2개의 배열을 바꾼다.
필요한 개수의 수를 배열에서 차례대로 가져온다.

int	aRanderm[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

int	loop_cnt = rand() % MAX_LOOP;
for(int i = 0; i<loop_cnt ; ++i)
{
	int	first = rand() % 10;
	int	second = rand() % 10;
	swap(aRanderm[first], aRanderm[second]);
}

for(int i = 0; i<NUMVER_CNT ; ++i)
{
	cout << aRanderm[i] ;
}

cout << endl;

위와 비슷하게 한다는 말씀인것 같습니다만. 맞나요?

고작 블로킹 하나, 고작 25점 중에 1점, 고작 부활동
"만약 그 순간이 온다면 그때가 네가 배구에 빠지는 순간이야"

espoirgod의 이미지

Quote:
중복된 수가 있는지 검사하는 루틴이 들어간다면 효율이 굉장히 떨어지게 됩니다. 애초에 중복해서 뽑지 않는것이 가장 바람직하죠.

애초부터 중복해서 뽑지 않는다고 하셨는데.. 어떻게 할 수 있나요?
만약에 어떤 특정한 범위에 해당하는 수만 뽑는 경우라면
j0nguk 님께서 언급하셨던 것처럼 배열에 저장한 후
뽑은 것은 '배열의 끝 - M' 으로 차례대로 옮기고
발생가능한 난수가 0 ~ N 이라고 했다면
이후에 발생가능한 난수를 0 ~ (N - M) 이라고 하면 되겠지만..
(여기서 M 은 생성된 난수의 개수)

10진수의 각 숫자가 한번씩만 나오는 10자리의 숫자를 생성하는 알고리즘을 위해서
그 많은 숫자들을 배열에 저장할 수는 없는 것 아닌가요?
(즉, 0 ~ 9 가 오직 1번씩만 사용되어야 함)
중복 검사를 피하려면, 엄청난 양의 메모리를 낭비해야만
하는 것처럼 보이는데... 맞나요?

ps.
랜덤함수의 원리에 대해서 공부를 하지 않아서 잘 모르겠습니다만....
0 ~ 9 까지의 숫자를 임의로 생성하는 경우에 최소한 몇 번 이내에
각 숫자가 적어도 1번은 발생한다라는 보장을 할 수 있나요?
랜덤함수도 수학 공식이던데... 음... 공부하지 않고...
쉽게 얻으려고 해서 죄송합니다 ㅡ0ㅡ;;

배움의 목적을 잊지 말자!!

herblover의 이미지

조인시 위키: article_random
를 참고해 보시면 도움이 될 것 같네요.
random(), srandom(), /dev/random 등에 대해서.. 상당히 충실히 설명하고 있는 문서 입니다.

espoirgod의 이미지

herblover 님 감사합니다~~~ :o

배움의 목적을 잊지 말자!!

espoirgod의 이미지

wpcasper 님 감사합니다.

그런데, 결과적으로 메모리를 많이 사용해야하네요;;

제가 처음에 올린 방법은 메모리를 적게 사용하는 대신에
연산의 수행 횟수를 가늠할 수 없다는게 문제가 되고..
(random 함수가 효율적이라면 큰 문제가 되지 않을듯합니다만..)

(위에서 계속 언급 되었었지만)지금 이 프로그램은 메모리를 많이 사용하는 대신에, 랜덤함수를 자리수 만큼만 실행하면 되는거네요.

시간과 공간이 반비례.......;;;

추가 질문 하나만 더 할께요;;
0 ~ 9999 까지의 수를 최소한의 메모리와 최소한의 연산(비교 등)으로
임의로 생성하려면 어떻게 해야할까요..?

(즉.. A 라는 파일에 저장된다고 할 때, A 에는 0 ~ 9999 까지의 숫자가 임의의 순서로 저장이 되는 겁니다)

배열에 0 ~ 9999 까지의 수를 저장한 후 생성하면 10000번 만에 연산이 끝나겠지만
'10000 * 정수를 저장하는데 필요한 바이트 수' 만큼의 메모리가 필요하고...

제가 처음에 올렸던 방법처럼 수를 생성하면 1250바이트 가 필요하지만 몇 번 실행될 지 알 수 없는 프로그램이 되버리고..

어떤 방법이 있을까요?

ps.
지금 당장 어디에 써야해서 의견을 여쭤보는 것이 아니라
그냥 궁금해서 계속 글을 올리는거예요 ㅎ_ㅎ;;;

배움의 목적을 잊지 말자!!

wpcasper의 이미지

썼다가 너무 장황해서 코드로 작성했는데 그사이에 보셨군요;;
펄입니다.

뽑고자 하는 숫자($req_num)가 N자리이고, 영부터 구까지의 숫자로 이루어진 10진법의 정수일때

@ary = {영, 일, 이, 삼, 사, 오, 육, 칠, 팔, 구};
for(i=0;i<N;i++){
    $pick = int(rnd() * (10-i));
	//랜덤한 정수를 선택합니다.
    $numeral= $ary[$pick];
	//랜덤한 정수가 선택한 배열에 있는 숫자를 선택합니다.

    $temp = $ary[$pick];
    $ary[$pick] = $ary[10-i];
    $ary[10-i] = $temp;
	//사용한 배열과 가장 마지막 배열의 원소를 서로 바꿉니다.

    $req_num= $req_num & $numeral;
	//위에서 선택한 숫자를 $req_num에 문자열 형태로 붙여 넣습니다.

}
print "$req_num";
wpcasper의 이미지

espoirgod wrote:
wpcasper 님 감사합니다.

추가 질문 하나만 더 할께요;;
0 ~ 9999 까지의 수를 최소한의 메모리와 최소한의 연산(비교 등)으로
임의로 생성하려면 어떻게 해야할까요..?

(즉.. A 라는 파일에 저장된다고 할 때, A 에는 0 ~ 9999 까지의 숫자가 임의의 순서로 저장이 되는 겁니다)

배열에 0 ~ 9999 까지의 수를 저장한 후 생성하면 10000번 만에 연산이 끝나겠지만
'10000 * 정수를 저장하는데 필요한 바이트 수' 만큼의 메모리가 필요하고...

제가 처음에 올렸던 방법처럼 수를 생성하면 1250바이트 가 필요하지만 몇 번 실행될 지 알 수 없는 프로그램이 되버리고..

어떤 방법이 있을까요?

0~9999 정수를 1개를 생산하든 1만개를 생산하든 필요한 배열의 크기는 10입니다.(10진수이기 때문에)
제가 배열의 크기와 메모리 관계를 잘 몰라서 그러는데 위에서 하신것처럼 공간 하나의 크기가 4바이트라면 총 필요한 공간은 40바이트입니다.

espoirgod의 이미지

Quote:
0~9999 정수를 1개를 생산하든 1만개를 생산하든 필요한 배열의 크기는 10입니다.(10진수이기 때문에)

파일 A에 0 ~ 9999 까지의 수를 출력합니다.
즉 파일 A 에는 10000개의 서로 다른 숫자가 저장됩니다.
이 조건에 부합한다는 말씀이신가요?
(아닌 것 같아서 다시 여쭤보는거예요;;)

숫자를 위와 같이 생성하면 중복되지 않나요..?

ps.
제가 이 얘기 하다가 저 얘기 하고 그래서 좀 해깔리신 것 같아요;;

배움의 목적을 잊지 말자!!

wpcasper의 이미지

주제와 같은 문제들은 보통 이번주에 사야할 로또복권의 번호를 알려주는 프로그램에 많이 사용됩니다. espoirgod 님께서 내주신 문제는 로또의 번호가 1~10번(10번은 0과 같습니다.)까지 있을때, 4개를 고르는 방법의 가지수.
즉, 로또 4/10 게임에서 구매자가 OMR카드를 칠하는 방법을 구하는 것과 동일합니다.

임의의 수를 랜덤하게 뽑아서 이미 나온것과 비교하는 것은 실제로 4개 이런것을 추출할때는 별로 문제가 되지 않습니다.

그러나, 골라야할 번호와 고르는 개수가 똑같을때는 문제가 생깁니다. 즉, 로또 45/45와 같은 경우에는 번호를 골라가면 골라갈수록(45번째 번호를 고르는것에 가까워질수록) 뽑지않은 번호를 고를 확률이 내려갑니다. 즉, 마지막 번호를 고를 확률은 1/45입니다.

만약 로또 10000/10000을 해야한다면? 마지막 확률은 1/10000 이고, 그 직전은 2/10000 이고... 무한루프에 빠지게 될 가능성이 짙습니다.
그래서 위와 같은 알고리즘은 사용하지 않는것이 좋습니다.

wpcasper의 이미지

espoirgod wrote:
Quote:
0~9999 정수를 1개를 생산하든 1만개를 생산하든 필요한 배열의 크기는 10입니다.(10진수이기 때문에)

파일 A에 0 ~ 9999 까지의 수를 출력합니다.
즉 파일 A 에는 10000개의 서로 다른 숫자가 저장됩니다.
이 조건에 부합한다는 말씀이신가요?
(아닌 것 같아서 다시 여쭤보는거예요;;)

숫자를 위와 같이 생성하면 중복되지 않나요..?

ps.
제가 이 얘기 하다가 저 얘기 하고 그래서 좀 해깔리신 것 같아요;;

아; 죄송합니다. 제가 착각을 했습니다.
시간은 유한하지만 메모리는 무한합니다...... 털썩;; 패닉;;;

wpcasper의 이미지

메모리 부족이 문제되신다면 M$社처럼 사용자에게 시스템 업그레이드를 권장하시면됩니다;; 털썩;;

espoirgod의 이미지

wpcasper님, 밤늦게 감사합니다~~ㅠㅠ

제가 좀 쓸데없는 것에 관심이 많아서요;;;
아직 모르는게 많아서 이렇게 한심한 주제를... ㅡ0ㅡ;;;

배움의 목적을 잊지 말자!!

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.