rango - generar numeros aleatorios negativos en c++
¿Cómo generar un número aleatorio en C++? (9)
Intento hacer un juego con dados, y necesito tener números aleatorios (para simular los lados del dado. Sé cómo hacerlo entre 1 y 6). Utilizando
#include <cstdlib>
#include <ctime>
#include <iostream>
using namespace std;
int main()
{
srand((unsigned)time(0));
int i;
i = (rand()%6)+1;
cout << i << "/n";
}
no funciona muy bien, porque cuando ejecuto el programa varias veces, aquí está la salida que obtengo:
6
1
1
1
1
1
2
2
2
2
5
2
Así que quiero un comando que genere un número aleatorio diferente cada vez, no el mismo 5 veces seguidas. ¿Hay algún comando que haga esto?
Genere un número aleatorio diferente cada vez, no el mismo seis veces seguidas.
Caso de uso
Comparé el problema de Predictabilidad con una bolsa de seis pedazos de papel, cada uno con un valor de 0 a 5 escrito en él. Se extrae un trozo de papel de la bolsa cada vez que se requiere un nuevo valor. Si la bolsa está vacía, los números vuelven a colocarse en la bolsa.
... a partir de esto, puedo crear un tipo de algoritmo.
Algoritmo
Una bolsa es generalmente una Collection
. Elegí un bool[]
(también conocido como matriz booleana, plano de bits o mapa de bits) para tomar el rol de la bolsa.
La razón por la que elegí un bool[]
es porque el índice de cada elemento ya es el valor de cada hoja de papel. Si los documentos requieren algo más escrito en ellos, entonces habría utilizado un Dictionary<string, bool>
en su lugar. El valor booleano se utiliza para realizar un seguimiento de si el número se ha dibujado o no.
Un contador llamado RemainingNumberCount
se inicializa a 5
que cuenta hacia abajo cuando se elige un número aleatorio. Esto nos ahorra tener que contar cuántos pedazos de papel quedan cada vez que deseamos dibujar un nuevo número.
Para seleccionar el siguiente valor aleatorio, estoy usando un for..loop
para buscar en la bolsa de índices, y un contador para contar cuando un index
es false
llamado NumberOfMoves
.
NumberOfMoves
se utiliza para elegir el siguiente número disponible. NumberOfMoves
se establece primero como un valor aleatorio entre 0
y 5
, porque hay 0..5 pasos disponibles que podemos realizar a través de la bolsa. En la siguiente iteración, NumberOfMoves
se establece como un valor aleatorio entre 0
y 4
, porque ahora hay 0..4 pasos que podemos hacer a través de la bolsa. A medida que se usan los números, los números disponibles se reducen, por lo que usamos rand() % (RemainingNumberCount + 1)
para calcular el siguiente valor para NumberOfMoves
.
Cuando el contador NumberOfMoves
llega a cero, el for..loop
debe ser el siguiente:
- Establezca el valor actual para que sea el mismo que para el índice
for..loop
. - Establezca todos los números en la bolsa a
false
. - Descanso del para ...
for..loop
.
Código
El código para la solución anterior es el siguiente:
(ponga los siguientes tres bloques en el archivo principal .cpp uno después del otro)
#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;
}
};
Una clase de consola
Creo esta clase de consola porque facilita la redirección de la salida.
Debajo en el código ...
Console::WriteLine("The next value is " + randomBag.ToString());
...puede ser reemplazado por...
std::cout << "The next value is " + randomBag.ToString() << std::endl;
... y luego esta clase de Console
se puede eliminar si lo desea.
class Console {
public:
static void WriteLine(std::string s) {
std::cout << s << std::endl;
}
};
Método principal
Ejemplo de uso de la siguiente manera:
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");
}
Ejemplo de salida
Cuando ejecuté el programa, obtuve el siguiente resultado:
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 . . .
Frase de cierre
Este programa fue escrito usando Visual Studio 2017 , y elegí convertirlo en un proyecto de Visual C++ Windows Console Application
usando .Net 4.6.1
.
No estoy haciendo nada especial aquí, por lo que el código también debería funcionar en versiones anteriores de Visual Studio.
Aquí hay un generador aleatorio simple con aprox. probabilidad igual de generar valores positivos y negativos alrededor de 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;
}
El problema más importante de su aplicación de prueba es que llame a srand
una vez y luego llame al rand
una vez y salga.
El objetivo de la función srand
es inicializar la secuencia de números pseudoaleatorios con una semilla aleatoria. Significa que si pasas el mismo valor a srand
en dos aplicaciones diferentes (con la misma implementación de srand
/ rand
) obtendrás exactamente la misma secuencia de valores rand()
leídos después de eso. Pero su secuencia pseudoaleatoria consta de un solo elemento: su salida consiste en los primeros elementos de diferentes secuencias pseudoaleatorias sembradas con tiempo de precisión de 1 segundo. Entonces, ¿qué esperas ver? Cuando ejecutas la aplicación en el mismo segundo, tu resultado es el mismo por supuesto (como Martin York ya mencionó en un comentario a la respuesta).
En realidad, debes llamar a srand(seed)
una vez y luego llamar a rand()
muchas veces y analizar esa secuencia: debe verse al azar.
El uso de módulo puede introducir un sesgo en los números aleatorios, dependiendo del generador de números aleatorios. Vea esta pregunta para más información. Por supuesto, es perfectamente posible obtener números repetitivos en una secuencia aleatoria.
Pruebe algunas características de C ++ 11 para una mejor distribución:
#include <random>
#include <iostream>
int main()
{
std::mt19937 rng;
rng.seed(std::random_device()());
std::uniform_int_distribution<std::mt19937::result_type> dist6(1,6); // distribution in range [1, 6]
std::cout << dist6(rng) << std::endl;
}
Consulte esta pregunta / respuesta para obtener más información sobre C ++ 11 números aleatorios. Lo anterior no es la única manera de hacer esto, pero es de una manera.
Este código produce números aleatorios de n
a m
.
int random(int n, int m){
return rand() % (m - n + 1) + n;
}
ejemplo:
int main(){
srand(time(0));
for(int i = 0; i < 10; i++)
cout << random(0, 99);
}
Si está utilizando libs de boost , puede obtener un generador aleatorio de esta manera:
#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;
}
}
Donde la función current_time_nanoseconds()
da la hora actual en nanosegundos que se usa como una semilla.
Aquí hay una clase más general para obtener enteros y fechas aleatorias en un rango:
#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);
}
};
para al azar cada archivo 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);
}
Aquí hay una solución. Cree una función que devuelva el número aleatorio y colóquelo fuera de la función principal para hacerlo global. Espero que esto ayude
#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;
}
#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;
}