C ++에서 난수를 생성하는 방법은 무엇입니까?


150

주사위로 게임을 만들려고하는데 주사위의 측면을 시뮬레이션하기 위해 임의의 숫자가 필요합니다 .1과 6 사이에서 만드는 법을 알고 있습니다. 사용

#include <cstdlib> 
#include <ctime> 
#include <iostream>

using namespace std;

int main() 
{ 
    srand((unsigned)time(0)); 
    int i;
    i = (rand()%6)+1; 
    cout << i << "\n"; 
}

프로그램을 몇 번 실행하면 다음과 같은 결과가 나오기 때문에 잘 작동하지 않습니다.

6
1
1
1
1
1
2
2
2
2
5
2

그래서 매번 다른 난수 를 생성하는 명령이 필요합니다 . 이를 수행 할 명령이 있습니까?


56
분포 문제는 제쳐두고 임의의 숫자를 사용하면 동일한 결과를 여러 번 연속으로 얻을 수 있습니다. 같은 숫자를 두 번 연속으로 얻지 못한다고 보장되면 결과가 실제로 무작위가 아니겠습니까?
cdhowie

5
그 숫자가 무작위가 아니라고 생각하는 이유는 무엇입니까? 진짜로 주사위를 던지면 그 결과를 얻을 수 있습니다. 만약 그들이 각각의 투구 사이에 다르다는 것이 보장된다면 그것은 실제로 무작위가 아닐 것입니다.
mattjgalloway

2
또한 모듈러스 연산자를 사용하는 것이 종종 좋은 생각이 아닌 이유를 eternalconfconfled.com/arts/jsw_art_rand.aspx에서 읽으십시오 .
벤자민 바니에

4
당신은 하나 이상의 의견이나 답변에 맞는 오해를하고 있습니다. 의사 난수 생성기, 시드, 진정으로 무작위 시드 선택의 중요성 및 균일 한 분포에 대해 독립적으로 학습해야합니다.
Kerrek SB

20
시간과 씨를 뿌릴 때. 이것은 또한 1 초에 한 번 이상 프로그램을 실행하면 같은 숫자를 얻게된다는 것을 의미합니다.
Martin York

답변:


79

테스트 응용 프로그램의 가장 근본적인 문제는 srand한 번만 호출 한 다음 한 번만 호출 rand하고 종료 한다는 것 입니다.

전체 srand기능 포인트 는 임의의 시드 로 의사 난수의 시퀀스 를 초기화 하는 것 입니다.

즉 , 동일한 / 구현을 사용하여 두 개의 다른 응용 프로그램에서 동일한 값 을 전달 하면srand 두 응용 프로그램에서 동일한 값 시퀀스 를 읽습니다.srandrandrand()

그러나 귀하의 응용 프로그램에서 의사 랜덤 시퀀스는 하나의 요소로 구성됩니다-현재의 시간과 같은 시드에서 생성 된 의사 랜덤 시퀀스의 첫 번째 요소 second 정밀도 . 그러면 출력에서 ​​무엇을 볼 것으로 예상됩니까?

분명히 동일한 초에 응용 프로그램을 실행할 때 동일한 시드 값을 사용하므로 결과는 물론 동일합니다 (마틴 요크가 이미 질문에 대한 의견에서 언급했듯이).

실제로 srand(seed)한 번 호출 한 다음 rand() 여러 번 호출 하고 해당 시퀀스를 분석해야합니다. 무작위로 표시되어야합니다.

편집하다:

아, 알겠습니다. 분명히 구두 설명으로는 충분하지 않습니다 (언어 장벽 또는 무언가 ... :)).

확인. srand()/rand()/time()질문에 사용 된 것과 동일한 함수를 기반으로하는 구식 C 코드 예제 :

#include <stdlib.h>
#include <time.h>
#include <stdio.h>

int main(void)
{
    unsigned long j;
    srand( (unsigned)time(NULL) );

    for( j = 0; j < 100500; ++j )
    {
        int n;

        /* skip rand() readings that would make n%6 non-uniformly distributed
          (assuming rand() itself is uniformly distributed from 0 to RAND_MAX) */
        while( ( n = rand() ) > RAND_MAX - (RAND_MAX-5)%6 )
        { /* bad value retrieved so get next one */ }

        printf( "%d,\t%d\n", n, n % 6 + 1 );
    }

    return 0;
}

^^^ THAT 프로그램의 단일 실행의 순서는 무작위로보고되어있다.

EDIT2 :

C 또는 C ++ 표준 라이브러리를 사용할 때는 현재 표준 데이터에 의해 결정된 임의의 데이터를 실제로 생성하는 단일 표준 함수 나 클래스가 없다는 것을 이해하는 것이 중요합니다. 이 문제에 접근하는 유일한 표준 도구는 std :: random_device 인데 불행히도 여전히 실제 임의성을 보장하지는 않습니다.

응용 프로그램의 특성에 따라 실제로 임의의 (예측할 수없는) 무작위 데이터가 필요한지 결정해야합니다. 가장 확실하게 진정한 무작위성이 필요한 경우 주목할만한 사례 이 는 정보 보안입니다 (예 : 대칭 키, 비대칭 개인 키, 솔트 값, 보안 토큰 등 생성).

그러나 보안 등급 난수는 별도의 기사를 쓸 가치가있는 별도의 산업입니다.

대부분의 경우 의사 난수 생성기 는 충분합니다 (예 : 과학 시뮬레이션 또는 게임). 어떤 경우에는 일관되게 정의 된 의사 랜덤 시퀀스가 ​​필요합니다. 예를 들어 게임에서는 많은 데이터를 저장하지 않기 위해 런타임에 정확히 동일한 맵을 생성하도록 선택할 수 있습니다.

원래의 질문과 다수의 동일 / 유사한 질문 (및 그에 대한 많은 오도 된 "답변")은 가장 먼저 난수를 의사 난수와 구별하고 의사 난수 숫자 시퀀스가 ​​무엇인지 이해하는 것이 중요하다는 것을 나타냅니다 의사 난수 생성기는 실제 난수 생성기를 사용할 수있는 것과 같은 방식으로 사용되지 않습니다.

직관적으로 난수를 요청할 때 반환 된 결과는 이전에 반환 된 값에 의존해서는 안되며 누군가가 이전에 요청한 적이 있고 어떤 순간과 프로세스에 따라 어떤 컴퓨터와 발전기에서 어떤 컴퓨터에 의존하는지에 의존해서는 안됩니다 요청 된 은하계 그것은 "무작위" 라는 단어가 결국 예측할 수없고 무엇이든 독립적이라는 의미입니다. 그렇지 않으면 더 이상 무작위가 아닙니다. 이 직관을 사용하면 가능한 어떤 맥락에서든 임의의 숫자를 얻기 위해 캐스팅 할 마법 주문을 웹에서 검색하는 것이 당연합니다.

^^^ 그런 종류의 직관적 인 기대는 매우 잘못되어 의사 난수 생성기를 포함하는 모든 경우에 해 롭습니다 . 합니다. 실제 난수에 대해서도 합리적입니다.

"임의의 숫자"라는 의미가 있지만 "의사 난수"와 같은 것은 없습니다. 의사 난수 생성기는 실제로 의사 난수 생성 순서를 .

전문가가 PRNG의 품질에 대해 이야기 할 때 실제로 생성 된 시퀀스의 통계적 특성 (및 주목할만한 하위 순서)에 대해 이야기합니다. 예를 들어, 두 개의 고품질 PRNG를 차례로 사용하여 결합하는 경우 각각 좋은 시퀀스를 개별적으로 생성하더라도 나쁜 결과 시퀀스를 생성 할 수 있습니다 (두 개의 우수한 시퀀스는 서로 단순히 상관되어 잘못 결합 될 수 있음).

의사-랜덤 시퀀스는 실제로 항상 결정적입니다 (알고리즘과 초기 매개 변수로 미리 결정됨 ).

특히 rand()/ srand(s)함수 쌍은 구현 정의 알고리즘으로 생성 된 단일 프로세스 비스 레드 안전 (!) 의사 난수 시퀀스를 제공합니다. 함수 rand()는 범위 내의 값을 생성합니다[0, RAND_MAX] .

C11 표준에서 인용 :

srand함수는 인수를 시드로 사용하여에 대한 후속 호출에 의해 반환되는 새로운 의사 난수 시퀀스를 생성합니다 rand. srand그런 다음 동일한 시드 값으로 호출 되면 의사 난수 시퀀스가 ​​반복됩니다. 경우 rand모든 호출을하기 전에 호출 srand되었을 동일한 순서로 생성 될 때한다 srand제 1 시드 값이라고한다.

많은 사람들이 합리적으로 그 예상 rand()범위에 반 독립적 인 균일하게 분포 된 일련의 숫자를 생산하는 것 0까지 RAND_MAX. 가장 확실하게 (그렇지 않으면 쓸모가 없지만) 표준은 그럴 필요 가 없습니다 . "임의의 무작위 서열의 품질에 대한 보장은 없습니다"라는 명시 적 부인도 있습니다. 일부 역사적인 경우 rand/ srand구현은 실제로 품질이 매우 좋지 않았습니다. 현대적인 구현에서는 충분하지만 신뢰는 깨졌고 복구하기 쉽지 않습니다. 스레드에 안전하지 않은 특성 외에도 멀티 스레드 응용 프로그램에서 안전하고 까다 롭고 제한적입니다 (아직도 하나의 전용 스레드에서 사용할 수 있음).

새로운 클래스 템플릿 std :: mersenne_twister_engine <> (및 편리한 템플릿 매개 변수 조합을 갖춘 편리한 typedef-std::mt19937 / std::mt19937_64)은 C ++ 11 표준에 정의 된 객체 별 의사 난수 생성기를 제공합니다. 동일한 템플릿 매개 변수와 동일한 초기화 매개 변수를 사용하면 C ++ 11 호환 표준 라이브러리로 구축 된 모든 응용 프로그램의 모든 컴퓨터에서 서로 다른 개체가 동일한 개체 별 출력 시퀀스를 생성합니다. 이 클래스의 장점은 예측 가능한 고품질 출력 시퀀스와 모든 구현에서 일관성이 있다는 것입니다.

또한 C ++ 11 표준 -std :: linear_congruential_engine <> (역사적 srand/rand으로 일부 C 표준 라이브러리 구현에서 공정한 품질 알고리즘으로 사용됨 ) 및 std :: subtract_with_carry_engine <>에 정의 된 PRNG 엔진이 더 있습니다. 또한 완전히 정의 된 매개 변수 종속 오브젝트 별 출력 시퀀스를 생성합니다.

위의 쓸모없는 C 코드에 대한 현대 C ++ 11 대체 예제 :

#include <iostream>
#include <chrono>
#include <random>

int main()
{
    std::random_device rd;
    // seed value is designed specifically to make initialization
    // parameters of std::mt19937 (instance of std::mersenne_twister_engine<>)
    // different across executions of application
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);

    for( unsigned long j = 0; j < 100500; ++j )
    /* ^^^Yes. Generating single pseudo-random number makes no sense
       even if you use std::mersenne_twister_engine instead of rand()
       and even when your seed quality is much better than time(NULL) */    
    {
        std::mt19937::result_type n;
        // reject readings that would make n%6 non-uniformly distributed
        while( ( n = gen() ) > std::mt19937::max() -
                                    ( std::mt19937::max() - 5 )%6 )
        { /* bad value retrieved so get next one */ }

        std::cout << n << '\t' << n % 6 + 1 << '\n';
    }

    return 0;
}

std :: uniform_int_distribution <> 을 사용하는 이전 코드 버전

#include <iostream>
#include <chrono>
#include <random>

int main()
{
    std::random_device rd;
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);
    std::uniform_int_distribution<unsigned> distrib(1, 6);

    for( unsigned long j = 0; j < 100500; ++j )
    {
        std::cout << distrib(gen) << ' ';
    }

    std::cout << '\n';
    return 0;
}

나는 여기 링크 에서 비슷한 질문을 했지만 여전히 명확한 대답을 찾지 못했습니다. "실제로 srand (seed)를 한 번 호출 한 다음 rand ()를 호출해야합니다."라는 코드를 보여 주어야합니다.
bashburak

2
@bashburak이 답변의 요점을 완전히 놓친 것 같습니다. 왜 내 견적을 정확히 깎았습니까? 나는 내 대답에서 말 그대로 "실제로 srand (seed)를 한 번 호출 한 다음 rand ()를 여러 번 호출 하고 그 순서를 분석해야합니다. 무작위로 보일 것입니다." 단일 srand (...) 호출 후 rand () MANY TIMES를 호출해야한다는 것을 알고 계셨습니까? 귀하의 링크에있는 귀하의 질문은이 질문과 정확히 똑같은 오해입니다.
Dundich Serge

이것은 오래된 답변이지만 Google "C ++ 난수 생성"에 표시됩니다. 당신이 사용하는 조언 때문에, C ++ 프로그래머를위한 가난한 조언을 rand()하고 srand(). 업데이트 할 수 있습니까?
Yakk-Adam Nevraumont

@ Yakk-AdamNevraumont 실제로 rand()및 사용을 권장하지 않습니다 srand(). 실제로 그것은 제공된 설명과 함께 질문에 대답합니다. 의사 난수 생성의 기본 개념이 의사 난수 시퀀스와 그 시드의 의미와 같이 설명되어야한다는 것은 ( rand/ 를 사용하는) 설명에서 분명 srand합니다. 나는 정확히 그렇게하고 가장 간단하고 친숙한 rand/ srand조합을 사용하려고합니다 . 재미있는 점은 매우 큰 평가를 받았더라도 질문의 작성자와 동일한 오해로 인해 다른 답변이 발생한다는 것입니다.
던디 치

@ Yak-AdamNevraumont 나는 당신의 조언을 받아 최신 C ++ 추가에 대한 정보로 내 대답을 수정했습니다. 나는이 주제에 조금 떨어져 생각하지만 -하지만 당신의 제안뿐만 아니라 다른 대답은 모두 좋은 오래된 것을 나타냅니다 std::rand/std::srand및 새로운 C ++ 라이브러리 기능이 좋아 std::random_device<>, :: mersenne_twister_engine <> 표준 및 다수의 무작위 분포 약간의 설명이 필요합니다.
Dundich Serge

216

모듈로를 사용하면 난수 생성기에 따라 난수에 바이어스가 발생할 수 있습니다. 자세한 내용은이 질문을 참조하십시오. 물론 임의의 순서로 반복되는 숫자를 얻는 것이 완벽하게 가능합니다.

더 나은 배포를 위해 C ++ 11 기능을 사용해보십시오.

#include <random>
#include <iostream>

int main()
{
    std::random_device dev;
    std::mt19937 rng(dev());
    std::uniform_int_distribution<std::mt19937::result_type> dist6(1,6); // distribution in range [1, 6]

    std::cout << dist6(rng) << std::endl;
}

C ++ 11 난수에 대한 자세한 내용은이 질문 / 답변을 참조하십시오. 위의 방법이 유일한 방법은 아니지만 한 가지 방법입니다.


7
사용하여 도입되는 바이어스의 양은 %6거의 없습니다. 라스 베이거스에서 사용하기 위해 크랩 게임을 작성하는 경우 중요 할 수 있지만 거의 다른 상황에서는 아무런 영향을 미치지 않습니다.
핫 릭

9
HotLicks : 동의하지만 random_device, mt19937이미 지원 하고 이미 지원하는 C ++ 버전을 사용하고 있다면 말 그대로 표준을 사용 하지 않을 이유가 없습니다uniform_int_distribution .
Quuxplusone

4
모든 프로그래머는 부서를 사용하고 수백 클럭 사이클을 소비하고 애플리케이션 타이밍을 망가 뜨리거나 많은 배터리 전력을 소모 할 수 있기 때문에 전염병과 같은 모듈로를 피하도록 조언해야합니다.

3
rng는 "range"입니까?
Christoffer

4
@ ChristofferHjärtström : 그것은을 위해의 R andom n은 암갈색 g enerator.
Cornstalks

11

boost libs를 사용하는 경우 다음과 같은 방법으로 랜덤 생성기를 얻을 수 있습니다.

#include <iostream>
#include <string>

// Used in randomization
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>

using namespace std;
using namespace boost;

int current_time_nanoseconds(){
    struct timespec tm;
    clock_gettime(CLOCK_REALTIME, &tm);
    return tm.tv_nsec;
}

int main (int argc, char* argv[]) {
    unsigned int dice_rolls = 12;
    random::mt19937 rng(current_time_nanoseconds());
    random::uniform_int_distribution<> six(1,6);

    for(unsigned int i=0; i<dice_rolls; i++){
        cout << six(rng) << endl;
    }
}

함수 current_time_nanoseconds()가 시드로 사용되는 현재 시간을 나노초 단위로 제공합니다.


범위 내에서 임의의 정수와 날짜를 얻는 더 일반적인 클래스는 다음과 같습니다.

#include <iostream>
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/date_time/gregorian/gregorian.hpp"


using namespace std;
using namespace boost;
using namespace boost::posix_time;
using namespace boost::gregorian;


class Randomizer {
private:
    static const bool debug_mode = false;
    random::mt19937 rng_;

    // The private constructor so that the user can not directly instantiate
    Randomizer() {
        if(debug_mode==true){
            this->rng_ = random::mt19937();
        }else{
            this->rng_ = random::mt19937(current_time_nanoseconds());
        }
    };

    int current_time_nanoseconds(){
        struct timespec tm;
        clock_gettime(CLOCK_REALTIME, &tm);
        return tm.tv_nsec;
    }

    // C++ 03
    // ========
    // Dont forget to declare these two. You want to make sure they
    // are unacceptable otherwise you may accidentally get copies of
    // your singleton appearing.
    Randomizer(Randomizer const&);     // Don't Implement
    void operator=(Randomizer const&); // Don't implement

public:
    static Randomizer& get_instance(){
        // The only instance of the class is created at the first call get_instance ()
        // and will be destroyed only when the program exits
        static Randomizer instance;
        return instance;
    }
    bool method() { return true; };

    int rand(unsigned int floor, unsigned int ceil){
        random::uniform_int_distribution<> rand_ = random::uniform_int_distribution<> (floor,ceil);
        return (rand_(rng_));
    }

    // Is not considering the millisecons
    time_duration rand_time_duration(){
        boost::posix_time::time_duration floor(0, 0, 0, 0);
        boost::posix_time::time_duration ceil(23, 59, 59, 0);
        unsigned int rand_seconds = rand(floor.total_seconds(), ceil.total_seconds());
        return seconds(rand_seconds);
    }


    date rand_date_from_epoch_to_now(){
        date now = second_clock::local_time().date();
        return rand_date_from_epoch_to_ceil(now);
    }

    date rand_date_from_epoch_to_ceil(date ceil_date){
        date epoch = ptime(date(1970,1,1)).date();
        return rand_date_in_interval(epoch, ceil_date);
    }

    date rand_date_in_interval(date floor_date, date ceil_date){
        return rand_ptime_in_interval(ptime(floor_date), ptime(ceil_date)).date();
    }

    ptime rand_ptime_from_epoch_to_now(){
        ptime now = second_clock::local_time();
        return rand_ptime_from_epoch_to_ceil(now);
    }

    ptime rand_ptime_from_epoch_to_ceil(ptime ceil_date){
        ptime epoch = ptime(date(1970,1,1));
        return rand_ptime_in_interval(epoch, ceil_date);
    }

    ptime rand_ptime_in_interval(ptime floor_date, ptime ceil_date){
        time_duration const diff = ceil_date - floor_date;
        long long gap_seconds = diff.total_seconds();
        long long step_seconds = Randomizer::get_instance().rand(0, gap_seconds);
        return floor_date + seconds(step_seconds);
    }
};

1
우리가 표준의 일부로 무작위로 만들었으므로 실제로 오래된 컴파일러를 사용하지 않는 한 부스트 버전의 사용을 권장하지 않습니다.
마틴 요크

9
#include <iostream>
#include <cstdlib>
#include <ctime>

int main() {
    srand(time(NULL));
    int random_number = std::rand(); // rand() return a number between ​0​ and RAND_MAX
    std::cout << random_number;
    return 0;
}

http://en.cppreference.com/w/cpp/numeric/random/rand


질문 작성자 코드와의 차이점은 무엇입니까? (사용하지 않는 것을 제외 %6.) 그리고 당신이 사용하기로 결정하는 경우 std::rand의 C ++ API rand다음 C 라이브러리 함수를 왜 사용하지 std::timestd::srandC ++ 스타일의 일관성을 위해서?
Dundich

4

Randomer여기에서 난수를 생성하기위한 전체 클래스 코드를 얻을 수 있습니다!

프로젝트의 다른 부분에 임의의 숫자가 필요한 경우 별도의 클래스 Randomer를 만들어 프로젝트 random내부의 모든 내용 을 캡슐화 할 수 있습니다 .

그런 것 :

class Randomer {
    // random seed by default
    std::mt19937 gen_;
    std::uniform_int_distribution<size_t> dist_;

public:
    /*  ... some convenient ctors ... */ 

    Randomer(size_t min, size_t max, unsigned int seed = std::random_device{}())
        : gen_{seed}, dist_{min, max} {
    }

    // if you want predictable numbers
    void SetSeed(unsigned int seed) {
        gen_.seed(seed);
    }

    size_t operator()() {
        return dist_(gen_);
    }
};

이러한 수업은 나중에 편리 할 것입니다.

int main() {
    Randomer randomer{0, 10};
    std::cout << randomer() << "\n";
}

이러한 클래스를 사용하여 임의의 문자열을 생성 하는 방법을 예로 들어이 링크를 확인할 수 있습니다 Randomer. 원한다면 사용할 수도 있습니다 Randomer.


모든 Randomer 객체에 생성기를 재사용하지 않겠습니까? 특히 초기화를 만들고 상태를 유지하는 것이 상대적으로 비싸기 때문에 특히 그렇습니다.
마틴 요크

3

한 번에 같은 여섯 번이 아닌 매번 다른 난수를 생성하십시오.

사용 사례 시나리오

나는 Predictability의 문제를 6 비트의 종이 봉지에 비유했는데 각각 0에서 5까지의 값을 기록했습니다. 새로운 가치가 필요할 때마다 백에서 종이를 꺼냅니다. 가방이 비어 있으면 숫자가 가방에 다시 넣어집니다.

... 이로부터 일종의 알고리즘을 만들 수 있습니다.

연산

가방은 보통 Collection 입니다. 나는이 선택한 bool[]가방의 역할을하는 (그렇지 않으면 부울 배열, 비트 평면 또는 비트 맵라고도 함).

내가 선택한 이유는 bool[] 는 각 항목의 색인이 이미 각 종이의 가치이기 때문입니다. 만약 논문이 다른 어떤 것을 요구했다면 나는 Dictionary<string, bool>그 자리에서를 사용했을 것 입니다. 부울 값은 숫자가 아직 그려 졌는지 여부를 추적하는 데 사용됩니다.

호출 된 카운터 RemainingNumberCount5임의의 숫자가 선택되면 카운트 다운되는 . 이를 통해 새로운 숫자를 그릴 때마다 몇 장의 종이가 남아 있는지 계산할 필요가 없습니다.

다음 임의의 값을 선택하려면 a for..loop를 사용하여 인덱스 백을 스캔하고 a indexfalse호출 될 때 카운트 다운하는 카운터를 사용 NumberOfMoves합니다.

NumberOfMoves사용 가능한 다음 번호를 선택하는 데 사용됩니다. 백을 통해 수행 할 수있는 0..5 단계가 있으므로 먼저 와 ~ NumberOfMoves사이의 임의의 값으로 설정됩니다 . 다음 반복 에서는 백을 통해 할 수있는 0..4 단계가 있으므로 와 사이의 임의의 값으로 설정됩니다 . 숫자가 사용됨에 따라 사용 가능한 숫자가 줄어들어 대신에 대한 다음 값을 계산하는 데 사용 됩니다 .05NumberOfMoves04rand() % (RemainingNumberCount + 1)NumberOfMoves

NumberOfMoves카운터에 도달 제로의이 for..loop같은 다음을 수행해야합니다

  1. 현재 값을 for..loop의 색인 과 동일하게 설정하십시오 .
  2. 가방에있는 모든 숫자를로 설정하십시오 false.
  3. 에서 중단하십시오 for..loop.

암호

위 솔루션의 코드는 다음과 같습니다.

(다음 세 개의 블록을 기본 .cpp 파일에 차례로 배치하십시오)

#include "stdafx.h"
#include <ctime> 
#include <iostream>
#include <string>

class RandomBag {
public:
    int Value = -1;

    RandomBag() {
        ResetBag();

    }

    void NextValue() {
        int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);

        int NumberOfMoves = rand() % (RemainingNumberCount + 1);

        for (int i = 0; i < BagOfNumbersLength; i++)            
            if (BagOfNumbers[i] == 0) {
                NumberOfMoves--;

                if (NumberOfMoves == -1)
                {
                    Value = i;

                    BagOfNumbers[i] = 1;

                    break;

                }

            }



        if (RemainingNumberCount == 0) {
            RemainingNumberCount = 5;

            ResetBag();

        }
        else            
            RemainingNumberCount--; 

    }

    std::string ToString() {
        return std::to_string(Value);

    }

private:
    bool BagOfNumbers[6]; 

    int RemainingNumberCount;

    int NumberOfMoves;

    void ResetBag() {
        RemainingNumberCount = 5;

        NumberOfMoves = rand() % 6;

        int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);

        for (int i = 0; i < BagOfNumbersLength; i++)            
            BagOfNumbers[i] = 0;

    }

};

콘솔 클래스

출력을 쉽게 리디렉션 할 수 있기 때문에이 콘솔 클래스를 만듭니다.

아래 코드에서 ...

Console::WriteLine("The next value is " + randomBag.ToString());

...로 대체 될 수 있습니다 ...

std::cout << "The next value is " + randomBag.ToString() << std::endl; 

...이 Console클래스는 원하는 경우 삭제할 수 있습니다.

class Console {
public:
    static void WriteLine(std::string s) {
        std::cout << s << std::endl;

    }

};

주요 방법

다음과 같은 사용법 예 :

int main() {
    srand((unsigned)time(0)); // Initialise random seed based on current time

    RandomBag randomBag;

    Console::WriteLine("First set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nSecond set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nThird set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nProcess complete.\n");

    system("pause");

}

출력 예

프로그램을 실행할 때 다음과 같은 결과가 나타납니다.

First set of six...

The next value is 2
The next value is 3
The next value is 4
The next value is 5
The next value is 0
The next value is 1

Second set of six...

The next value is 3
The next value is 4
The next value is 2
The next value is 0
The next value is 1
The next value is 5

Third set of six...

The next value is 4
The next value is 5
The next value is 2
The next value is 0
The next value is 3
The next value is 1

Process complete.

Press any key to continue . . .

결산 명세서

이 프로그램은 Visual Studio 2017을 사용하여 작성되었으며를 사용하여 Visual C++ Windows Console Application프로젝트로 선택했습니다 .Net 4.6.1.

여기서 특별한 작업을 수행하지 않으므로 코드가 이전 버전의 Visual Studio에서도 작동합니다.


이것이 VS 2017 인 경우 최신 버전의 표준 라이브러리 ( en.cppreference.com/w/cpp/numeric/random)를 사용해야 합니다. 현재이 예제는 C 난수 라이브러리 함수와 "생성 된 난수 시퀀스의 품질에 대한 보장이 없습니다"를 사용합니다.
Robert Andrzejuk

3

random number generationC ++ 프로그래밍 언어로 기본 웹 검색을 할 때 마다이 질문이 일반적으로 가장 먼저 나타납니다! 의사 난수 생성 의 개념을 더 잘 설명하기 위해 반지에 모자를 던지고 싶습니다.웹 에서이 같은 질문을 필연적으로 검색 할 미래의 코더를 위해 C ++에서 !

기초

의사 난수 생성에는 속성이 대략 난수 와 유사한 일련의 숫자를 생성하는 결정적 알고리즘 을 이용하는 프로세스가 포함됩니다 . 나는 진정한 무작위성이 수학과 컴퓨터 과학에서 다소 이해하기 어려운 신비 이기 때문에 대략 비슷 하다고 말한다 . 그러므로 왜 의사 난수 라는 용어 가 좀 더 정확한 방식으로 사용됩니까?

실제로 PRNG를 사용할 pseudo-random number generator수 있으려면 먼저 시드 라고도하는 초기 값을 알고리즘에 제공해야합니다 . 그러나 시드는 알고리즘 자체 사용 하기 전에 한 번만 설정해야 합니다!

/// Proper way!
seed( 1234 ) /// Seed set only once...
for( x in range( 0, 10) ):
  PRNG( seed ) /// Will work as expected

/// Wrong way!
for( x in rang( 0, 10 ) ):
  seed( 1234 ) /// Seed reset for ten iterations!
  PRNG( seed ) /// Output will be the same...

따라서 좋은 수의 시퀀스를 원하면 PRNG에 충분한 시드를 제공해야합니다!

올드 C 웨이

C ++이 가지고있는 C의 하위 호환 표준 라이브러리 는 헤더 파일 에 있는 선형 합동 생성기 (congruential generator)를 사용cstdlib 합니다! 이 PRNG는 모듈 식 산술, 즉를 사용하는 빠른 알고리즘을 사용하는 불연속 조각 별 함수를 통해 작동합니다 modulo operator '%'. @Predictability의 원래 질문과 관련하여이 PRNG의 일반적인 사용법은 다음과 같습니다.

#include <iostream>
#include <cstdlib>
#include <ctime>

int main( void )
{
  int low_dist  = 1;
  int high_dist = 6;
  std::srand( ( unsigned int )std::time( nullptr ) );
  for( int repetition = 0; repetition < 10; ++repetition )
    std::cout << low_dist + std::rand() % ( high_dist - low_dist ) << std::endl;
  return 0;
}

C의 PRNG의 일반적인 사용법에는 다음과 같은 다양한 문제가 있습니다.

  1. std::rand()@Predictability가 원하는 방식으로 [1, 6] 사이의 숫자를 생성하는 등 주어진 범위 사이의 의사 난수를 적절하게 생성하기 위해서는 전체 인터페이스 가 매우 직관적이지 않습니다.
  2. 일반적인 사용은 비둘기 구멍 원리로 인해 의사 난수 std::rand()균일 한 분포 가능성 을 제거합니다. .
  3. 제한된 유형으로 간주 되기 때문에 기술적으로 std::rand()시드 되는 일반적인 방법 이 std::srand( ( unsigned int )std::time( nullptr ) )올바르지 않습니다 . 따라서에서 변환 에이 보장되지 않습니다!time_ttime_tunsigned int

C의 PRNG 사용에 대한 전반적인 문제 및이를 피하는 방법에 대한 자세한 정보는 rand () (C / C ++) 사용 : C 표준 라이브러리의 rand () 함수에 대한 조언을 참조하십시오 !

표준 C ++ 방식

ISO / IEC 14882 : 2011 표준, 즉 C ++ 11이 발표 된 이후로 random라이브러리는 현재 C ++ 프로그래밍 언어와 분리되어 있습니다. 이 라이브러리는 여러 PRNG와 균일 한 분포 , 정규 분포 , 이항 분포 등과 같은 다양한 분포 유형을 갖추고 있습니다 . 다음 소스 코드 예제는 @Predictability의 원래 질문과 관련 하여 라이브러리 의 매우 기본적인 사용법을 보여줍니다 .random

#include <iostream>
#include <cctype>
#include <random>

using u32    = uint_least32_t; 
using engine = std::mt19937;

int main( void )
{
  std::random_device os_seed;
  const u32 seed = os_seed();

  engine generator( seed );
  std::uniform_int_distribution< u32 > distribute( 1, 6 );

  for( int repetition = 0; repetition < 10; ++repetition )
    std::cout << distribute( generator ) << std::endl;
  return 0;
}

정수 값 의 균일 한 분포 를 가진 32 비트 Mersenne Twister 엔진 이 위의 예에서 사용되었습니다. (소스 코드의 엔진 이름은 2 ^ 19937-1 의 기간 에서 유래하기 때문에 이상하게 들립니다). 이 예제는 또한 엔진을 시드하는 데 사용 되며 운영 체제에서 값을 얻습니다 (Linux 시스템을 사용하는 경우 의 값 을 반환 함 ).std::random_devicestd::random_device/dev/urandom

엔진std::random_device 을 시드 하기 위해 사용할 필요는 없습니다 . 상수 또는 라이브러리를 사용할 수 있습니다 ! 또한 32 비트 버전의 엔진 을 사용할 필요가 없습니다 . 다른 옵션도 있습니다 ! 라이브러리 의 기능에 대한 자세한 내용은 cplusplus.com을 참조하십시오.chronostd::mt19937random

결국, C ++ 프로그래머는 std::rand()더 이상 나쁜 것을 사용 하지 말고 현재 표준이보다 직관적 이고 신뢰할 수있는 더 나은 대안을 제공하기 때문에 더 이상 사용 하지 않아야 합니다 . 다행스럽게도 많은 분들이이 정보가 도움이 되셨으며, 특히 최근 웹 검색을하신 분들에게 도움이 되길 바랍니다 generating random numbers in c++!


2

해결책은 다음과 같습니다. 난수를 반환하는 함수를 만들고 주 함수 외부에 배치하여 전역 화하십시오. 도움이 되었기를 바랍니다

#include <iostream>
#include <cstdlib>
#include <ctime>
int rollDie();
using std::cout;
int main (){
    srand((unsigned)time(0));
    int die1;
    int die2;
    for (int n=10; n>0; n--){
    die1 = rollDie();
    die2 = rollDie();
    cout << die1 << " + " << die2 << " = " << die1 + die2 << "\n";
}
system("pause");
return 0;
}
int rollDie(){
    return (rand()%6)+1;
}

2

이 코드는에서 임의의 숫자를 생성 n합니다 m.

int random(int from, int to){
    return rand() % (to - from + 1) + from;
}

예:

int main(){
    srand(time(0));
    cout << random(0, 99) << "\n";
}

2
이것은 실제로 질문에 대답하지 않습니다.
HolyBlackCat

1
당신은 그것을 고치지 않았습니다. 문제의 요점은 초당 여러 번 프로그램을 실행하면 동일한 임의의 값을 생성한다는 것입니다. 코드도 그렇게합니다.
HolyBlackCat

1
@HolyBlackCat 여러 번 실행했는지 확인했지만 작동합니다. srand(time(0))전에 주 기능에 추가 했습니까 random(n, m)?
Amir Fo

1
srand(time(0))for 루프 또는 함수 구현 내부가 아닌 주요 함수에 추가해야합니다 .
Amir Fo

1
코드를 그대로 복사했습니다. 초당 여러 번 실행 했습니까 ?
HolyBlackCat

1

임의의 모든 RUN 파일

size_t randomGenerator(size_t min, size_t max) {
    std::mt19937 rng;
    rng.seed(std::random_device()());
    //rng.seed(std::chrono::high_resolution_clock::now().time_since_epoch().count());
    std::uniform_int_distribution<std::mt19937::result_type> dist(min, max);

    return dist(rng);
}

1
생성기를 여러 번 만들지 않아도됩니다. 적절한 분포를 갖는 임의의 난수 시퀀스를 생성하여 무작위로 보이도록 여러 상태를 유지합니다.
Martin York

-2

다음은 대략적인 간단한 임의 생성기입니다. 0 주위에서 양수 및 음수 값을 생성 할 확률이 같습니다.

  int getNextRandom(const size_t lim) 
  {
        int nextRand = rand() % lim;
        int nextSign = rand() % lim;
        if (nextSign < lim / 2)
            return -nextRand;
        return nextRand;
  }


   int main()
   {
        srand(time(NULL));
        int r = getNextRandom(100);
        cout << r << endl;
        return 0;
   }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.