의사 난수란 무엇인가요?
범주: 컴퓨터/네트워크
분석:
컴퓨터에서 난수가 생성되는 방식에 대해 여러 번 논의했을 수 있습니다. 이 기사에서는 이에 대해 살펴보겠습니다. 문제를 더 자세히 설명하고 문제에 대한 나의 이해를 설명합니다.
가장 먼저 언급해야 할 점은 컴퓨터가 절대 난수를 생성하지 않는다는 것입니다. 컴퓨터는 "의사 난수"만 생성할 수 있습니다. 사실, 절대 난수는 이상적인 난수일 뿐입니다. 컴퓨터가 어떻게 발전하든 절대 난수 문자열을 생성하지는 않습니다. 컴퓨터는 상대적인 난수, 즉 의사 난수만 생성할 수 있습니다.
의사 난수는 의사 난수가 아닙니다. 여기서 "의사"는 규칙적인 것을 의미합니다. 즉, 컴퓨터에서 생성된 의사 난수는 무작위이면서 규칙적인 것입니다. 그것을 이해하는 방법? 생성된 의사 난수는 때때로 특정 규칙을 따르기도 하고 때로는 어떤 규칙도 따르지 않는 경우도 있습니다. 예를 들어, "세상에 같은 모양을 가진 두 개의 잎은 없습니다." 이것은 단지 사물의 특성, 즉 무작위성을 가리킬 뿐이지만, 모든 나무의 잎은 비슷한 모양을 가지고 있다는 것이 사물의 본성입니다. 즉, 섹스. 이러한 관점에서 보면 컴퓨터는 의사 난수만 생성할 수 있지만 절대적인 난수는 생성할 수 없다는 사실을 받아들일 것입니다.
그렇다면 컴퓨터에서 난수는 어떻게 생성됩니까? 난수는 "무작위 시드"에 의해 생성된다고 말할 수 있습니다. 예, 난수 시드는 난수를 생성하는 데 사용되는 숫자입니다. 컴퓨터에서 이러한 "임의 시드"는 부호 없는 정수입니다. 그렇다면 무작위 씨앗은 어디서 오는 걸까요?
아래의 C 프로그램을 살펴보세요:
rand01.c
#include
static unsigned int RAND_SEED;
unsigned int random(void)
{
RAND_SEED=(RAND_SEED*123+59)%65536;
return(RAND_SEED); p >
}
void random_start(void)
{
int temp[2];
movedata(0x0040, 0x006c ,FP_SEG(임시),FP_OFF(임시),4);
RAND_SEED=임시[0];
}
main()
{
unsigned int i,n;
random_start();
for(i=0;i<10;i++) p >
printf("%u\t",random());
printf("\n");
}
이것은 프로그램(rand01.c)은 난수 생성 과정을 자세히 설명합니다.
먼저, 메인 프로그램은 Random_start() 메서드를 호출합니다. 나는 Random_start() 메서드에서 이 문장에 매우 관심이 있습니다. p>
movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);
이 함수는 메모리 데이터를 이동하는 데 사용됩니다. 여기서 FP_SEG(세그먼트에 대한 원거리 포인터)는 임시 배열 세그먼트 주소를 가져옵니다. FP_OFF(오프셋에 대한 원거리 포인터) 함수는 임시 배열의 상대 주소를 가져오는 함수입니다. movedata 함수의 기능은 0040:006CH 저장소에 위치한 더블 워드를 넣는 것입니다. 단위를 배열 temp에 선언된 두 개의 저장 단위로 변환합니다. 이런 식으로 임시 배열을 통해 0040:006CH의 16비트 숫자를 RAND_SEED로 보낼 수 있습니다.
Random은 무작위 시드 RAND_SEED의 값을 기반으로 난수를 계산하는 데 사용됩니다.
RAND_SEED=(RAND_SEED*123+59)%65536;
는 난수를 계산하는 데 사용되는 방법입니다. 동일한 컴퓨터에 설치된 운영 체제가 다르더라도 컴퓨터마다 계산 방법이 다릅니다. Linux와 Windows에서 각각 시도해 보았습니다. 동일한 난수 시드가 이 두 운영 체제에서 서로 다른 난수를 생성하는데 이는 계산 방법이 다르다는 것을 보여줍니다.
이제 우리는 랜덤 시드가 어디서 얻어지는지 이해하고, 랜덤 시드에서 난수가 어떻게 계산되는지 알게 되었습니다. 그렇다면 왜 메모리의 0040:006CH에서 랜덤 시드를 가져와야 할까요? 0040:006CH에 무엇이 저장되어 있나요?
"컴퓨터 구성 원리 및 인터페이스 기술" 과정을 공부한 사람들은 Intel 8253 타이머/카운터가 ROM BIOS 클럭 인터럽트 서비스 프로그램을 프로그래밍할 때 사용된다는 것을 기억할 것입니다. 이는 Intel 8259 인터럽트와 유사합니다. 칩 통신을 통해 마더보드에서 초당 생성되는 18.2개의 인터럽트는 타이머/카운터 값을 기반으로 인터럽트 칩을 제어하는 프로세서에 의해 생성됩니다. 컴퓨터 마더보드에는 현재 시스템 시간을 계산하는 타이머/카운터가 있습니다. 모든 클록 신호 주기는 이 카운터의 값을 어디에 저장합니까? 맞습니다. 메모리의 0040:006CH에 있습니다. 실제로 이 메모리 공간은 다음과 같이 정의됩니다.
TIMER_LOW DW ? 주소는 0040:006CH
TIMER_HIGH DW ? ; 주소는 0040:006EH
TIMER_OFT DB ?; 주소는 0040:0070H
클럭 인터럽트 서비스 프로그램에서는 TIMER_LOW가 가득 찰 때마다 카운터도 가득 차면 카운터 값이 0으로 재설정됩니다. 즉, TIMER_LOW의 16비트 바이너리가 0으로 재설정되고 TIMER_HIGH가 1만큼 증가합니다.
movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);
rand01.c의 것은 정확히 두 개의 16비트 이진수 TIMER_LOW 및 TIMER_HIGH입니다. 임시 배열에 넣은 다음 RAND_SEED로 보내서 "무작위 시드"를 얻습니다.
이제 한 가지 확실한 점은 랜덤 시드가 시스템 시계, 정확하게 말하면 컴퓨터 마더보드의 타이머/카운터 메모리에 있는 카운트 값에서 나온다는 것입니다. 이런 식으로 이전 분석을 요약하고 이러한 결론을 프로그램에 적용하는 방법에 대해 논의합니다.
1. 난수는 특정 계산 방법에 따라 무작위 시드로 계산된 값입니다. 따라서 계산 방법이 확실하고 난수 시드가 확실한 한 생성되는 난수는 변경되지 않습니다.
다음 C++ 프로그램을 살펴보세요:
rand02.cpp
#include
#include
using 네임스페이스 std;
int main()
{
unsigned int seed=5;
srand(seed);
unsigned int r=rand();
cout< 편집자 주: 코드 결함이 있을 수 있습니다.
}
동일한 플랫폼 환경에서 , exe를 컴파일하고 생성한 후 실행할 때마다 표시되는 난수는 동일합니다. 이는 동일한 컴파일 플랫폼 환경에서 난수 시드에서 난수를 생성하는 계산 방법이 동일하기 때문입니다. 또한, 난수 시드도 동일하므로 생성되는 난수도 동일합니다.
2. 사용자 또는 제3자가 무작위 시드를 설정하지 않는 한, 기본적으로 무작위 시드는 시스템 시계(즉, 타이머/카운터의 값)에서 나옵니다.
p>
다음 C++ 프로그램을 살펴보세요:
rand03.cpp
#include
#include
using 네임스페이스 std ;
int main()
{
srand((unsigned)time(NULL));
unsigned int r=rand ();
cout< return 0;
}
여기서 사용자 및 다른 프로그램이 임의의 시드를 설정하지 않으면 시스템의 값 타이머/카운터는 임의의 시드로 사용됩니다. 따라서 동일한 플랫폼 환경에서 exe를 컴파일하고 생성한 후 실행할 때마다 표시되는 난수는 의사 난수입니다. 즉, 표시되는 결과는 다음과 같습니다. 실행할 때마다 다릅니다.
3. 제안: 프로그램에서 난수 시퀀스를 생성하려면 난수를 생성하기 전에 난수 시드를 한 번 이상 설정해야 합니다.
임의의 문자열을 생성하려면 다음 C++ 프로그램을 고려하세요:
rand04.cpp
#include
#include
네임스페이스 std 사용;
int main()
{
int rNum,m=20;
char *ch =new char[m];
for ( int i = 0; i 보시다시피, 무작위 시드는 for 루프를 사용하여 프로그램에서 여러 번 설정됩니다.
srand ((unsigned)time(NULL));
rNum=1+(int)((rand()/(double)RAND_MAX)*36); 무작위 값 찾기
스위치 (rNum){
사례 1: ch[i]='a';
break ;
사례 2: ch[i]='b' ;
중단 ;
사례 3: ch[i]='c';
중단 ;
사례 4: ch[ i]='d';
중단 ;
사례 5: ch[i]='e';
중단 ;
사례 6: ch[i]='f';
중단 ;
사례 7: ch[i]='g';
중단 ;
사례 8: ch[i]='h';
break ;
사례 9: ch[i]='i';
중단 ;
사례 10: ch[i]='j';
중단 ;
사례 11: ch[i]=' k';
중단 ;
사례 12: ch[i]='l';
중단 ;
사례 13: ch[i]='m';
중단 ;
사례 14: ch[i]='n';
중단 ;
사례 15: ch[i]='o';
break ;
사례 16: ch[i]='p';
break ;
사례 17: ch[i]='q';
break ;
사례 18: ch[i]='r';
중단 ;
사례 19: ch[i]='s';
중단 ;
사례 20: ch[i] ='t';
중단 ;
사례 21: ch[i]='u';
중단 ;
사례 22: ch[i]='v';
중단 ;
사례 23: ch[i]='w';
중단 ; p>
p>
사례 24: ch[i]='x';
break ;
사례 25: ch[i]='y';
중단 ;
사례 26: ch[i]='z';
중단 ;
<p> 사례 27:ch[i]='0';
중단;
사례 28:ch[i]='1';
중단 ;
사례 29:ch[i]='2';
중단;
사례 30:ch[i]='3'; p> p>
중단;
사례 31:ch[i]='4';
중단;
사례 32:ch[i ]= '5';
중단;
사례 33:ch[i]='6';
중단;
사례 34 :ch[i]='7';
중단;
사례 35:ch[i]='8';
중단;< /p >
case 36:ch[i]='9';
break;
}스위치 끝
cout< }end of for 루프
cout< return 0;
}
실행 결과에 표시된 임의 문자열의 각 문자는 동일합니다. 즉, 생성된 문자는 시퀀스는 무작위가 아니므로 for 루프에서 srand((unsigned)time(NULL)); 을 이동하여 for 문 앞에 배치해야 무작위 문자 시퀀스가 생성될 수 있으며 문자 시퀀스는 다음과 같습니다. 실행될 때마다 생성되는 값이 달라집니다(하하, 같을 수도 있지만 이런 일이 발생할 가능성은 너무 적습니다).
이런 방식으로 srand((unsigned)time(NULL));을 srand(2);로 변경하면 한 번의 실행에서 생성되는 문자 순서는 무작위이지만 각 실행에서 생성되는 문자 순서는 다음과 같습니다. 실행은 무작위입니다. 문자열은 동일합니다. 프로그램에서 srand 문장을 제거하는 경우에도 마찬가지입니다.
또한 타이머 컨트롤을 사용하여 프로그래밍할 때 이러한 상황이 발생할 수 있습니다. 동일한 시간 간격으로 생성된 난수 세트가 규칙적으로 나타나고 사용자의 이벤트가 생성됩니다. key 생성된 난수 세트가 비교적 무작위로 나타나는 이유는 무엇입니까? 위의 분석을 바탕으로 답을 빠르게 알아낼 수 있습니다. 타이머는 컴퓨터 시계 카운터에 의해 시간 간격을 정확하게 조절하는 컨트롤이기 때문에 시간 간격은 동일하며, 이와 같이 카운터 전후의 값의 차이도 동일합니다. 값이 선형이므로 무작위 시드도 선형이므로 생성된 난수도 규칙적입니다. 사용자 키 누름 이벤트에 의해 생성된 난수는 인간의 키 누름에 의해 이벤트가 발생하고 사람들이 엄격한 키 누름 시간 간격을 보장할 수 없기 때문에 실제로 더 무작위적입니다. 시간 간격이 다른 한, 1마이크로초마다 카운터 이전과 이후의 값 차이가 달라지므로, 랜덤 시드의 변화는 선형 패턴을 잃고 생성된 난수는 더욱 불규칙해집니다. 이런 방식으로 생성된 난수 세트는 더 무작위적입니다. 이는 다양한 저녁 파티의 복권 프로그램을 생각나게 합니다. 사람들이 행운의 시청자를 생성하기 위해 버튼을 누르는 데 익숙하다면 무작위성의 원칙이 잘 실현되고 결과가 더 공정해질 것입니다.
마지막으로 두 가지 핵심 사항을 요약합니다.
1. 컴퓨터의 의사 난수는 특정 계산 방법에 따라 무작위 시드에 의해 계산된 값입니다. 따라서 계산 방법이 확실하고 난수 시드가 확실한 한 생성되는 난수는 고정되어 있습니다.
2. 사용자나 제3자가 랜덤 시드를 설정하지 않는 한, 랜덤 시드는 기본적으로 시스템 시계에서 나옵니다.