Как сгенерировать случайное число в С++?
Я пытаюсь сделать игру с кубиками, и мне нужно иметь в ней случайные числа (имитировать стороны кубика. Я знаю, как сделать это между 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
Поэтому мне нужна команда, которая будет генерировать различное случайное число каждый раз, а не одно и то же 5 раз подряд. Есть ли команда, которая сделает это?
Ответы
Ответ 1
Наиболее фундаментальная проблема вашего тестового приложения заключается в том, что вы вызываете srand
один раз, а затем вызываете rand
один раз и выходите.
Весь смысл функции srand
заключается в инициализации последовательности псевдослучайных чисел случайным начальным числом. Это означает, что если вы передадите одно и то же значение в srand
в двух разных приложениях (с одной и той же реализацией srand
/rand
), вы получите одинаковую последовательность значений rand()
прочитанную после этого. Но ваша псевдослучайная последовательность состоит только из одного элемента - ваши выходные данные состоят из первых элементов различных псевдослучайных последовательностей, высеянных с точностью до 1 секунды. Так что вы ожидаете увидеть? Когда вы запускаете приложение в одну и ту же секунду, ваш результат, конечно же, одинаков (как уже упоминал Мартин Йорк в комментарии к ответу).
На самом деле вы должны вызвать srand(seed)
один раз, а затем вызвать rand()
много раз и проанализировать эту последовательность - она должна выглядеть случайной.
Ответ 2
Использование по модулю может вносить смещение в случайные числа в зависимости от генератора случайных чисел. Смотрите этот вопрос для получения дополнительной информации. Конечно, вполне возможно получить повторяющиеся числа в случайной последовательности.
Попробуйте некоторые функции С++ 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;
}
Смотрите этот вопрос/ответ для получения дополнительной информации о С++ 11 случайных чисел. Вышесказанное не единственный способ сделать это, но это один из способов.
Ответ 3
Если вы используете 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);
}
};
Ответ 4
#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
Ответ 5
Randomer
можно получить полный Randomer
класса Randomer
для генерации случайных чисел!
Если вам нужны случайные числа в разных частях проекта, вы можете создать отдельный класс 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
если хотите.
Ответ 6
Вот решение. Создайте функцию, которая возвращает случайное число и поместите его
вне основной функции, чтобы сделать ее глобальной. Надеюсь, что это поможет
#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;
}
Ответ 7
для случайных файлов 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);
}
Ответ 8
Каждый раз генерируйте разные случайные числа, а не одно и то же шесть раз подряд.
Сценарий использования
Я сравнил проблему предсказуемости с пакетом из шести кусочков бумаги, на каждом из которых было написано значение от 0 до 5. Кусок бумаги вытягивается из пакета каждый раз, когда требуется новое значение. Если сумка пуста, номера возвращаются обратно в сумку.
... из этого я могу создать алгоритм сортов.
Алгоритм
Сумка - это обычно Collection
. Я выбрал bool[]
(также известный как логический массив, битовая плоскость или битовая карта), чтобы взять на себя роль мешка.
Причина, по которой я выбрал bool[]
заключается в том, что индекс каждого элемента уже является значением каждого куска бумаги. Если бы бумаги требовали что-то еще, написанное на них, я бы использовал Dictionary<string, bool>
на его месте. Логическое значение используется для отслеживания того, было ли число нарисовано или нет.
Счетчик с именем RemainingNumberCount
инициализируется равным 5
который отсчитывается при выборе случайного числа. Это избавляет нас от необходимости подсчитывать, сколько листов бумаги остается каждый раз, когда мы хотим нарисовать новое число.
Чтобы выбрать следующее случайное значение, я использую for..loop
для сканирования пакета индексов и счетчик для отсчета, когда index
равен false
называется NumberOfMoves
.
NumberOfMoves
используется для выбора следующего доступного номера. NumberOfMoves
устанавливается на случайное значение от 0
до 5
, потому что есть 0,5 шага, которые мы можем сделать через пакет. На следующей итерации NumberOfMoves
устанавливается в случайное значение от 0
до 4
, потому что теперь мы можем сделать 0..4 шага через пакет. Поскольку числа используются, доступные числа уменьшаются, поэтому мы вместо этого используем rand() % (RemainingNumberCount + 1)
чтобы вычислить следующее значение для NumberOfMoves
.
Когда счетчик NumberOfMoves
достигает нуля, for..loop
должен for..loop
следующим образом:
- Установите текущее значение
for..loop
индексу for..loop
. - Установите все числа в сумке на
false
. - Перерыв из-за
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, потому что он облегчает перенаправление вывода.
Ниже в коде...
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.
Ответ 9
Этот код генерирует случайные числа от n
до m
.
int random(int from, int to){
return rand() % (to - from + 1) + from;
}
пример:
int main(){
srand(time(0));
cout << random(0, 99) << "\n";
}
Ответ 10
Вот простой генератор случайных чисел с ок. равная вероятность получения положительных и отрицательных значений около 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;
}