# Random number without using the external crate?

Feels super no-go to me, but should do it ^^

You could use the current time with a high precision in the same way, if that's better for you

Here is an example that shows how to print a raw pointer (as KillTheMule suggested):

``````let ptr = Box::into_raw(Box::new(123));
println!("ptr: {}", ptr as usize);
``````

You will notice that the number is always even -- since it is a memory address, it will be aligned to the size of an integer.

An easier way (in my opinion) to make a poor-mans-random-generator is to look at the current time when the program starts. This example find the current time, expresses it as a duration since 1970, and then extracts the number of nanoseconds elapsed in the current second:

``````use std::time::{SystemTime, UNIX_EPOCH};

fn main() {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.subsec_nanos();
println!("nanos: {}", nanos);
}
``````

So if the current time is 10:20:30.123456789, the "random" number will be 123456789. The number will always be between zero and 1 billion.

Your students can then proceed to use a bit of modulo arithmetic to get a random number in the desired range. That should be more than good enough for a guessing game

6 Likes

I seriously love this solution. I also love any excuse to use more random math.

I hope, a basic RNG will be included in the future. Until then you can use the following implementation.

``````
const KX: u32 = 123456789;
const KY: u32 = 362436069;
const KZ: u32 = 521288629;
const KW: u32 = 88675123;

pub struct Rand {
x: u32, y: u32, z: u32, w: u32
}

impl Rand{
pub fn new(seed: u32) -> Rand {
Rand{
x: KX^seed, y: KY^seed,
z: KZ, w: KW
}
}

// Xorshift 128, taken from German Wikipedia
pub fn rand(&mut self) -> u32 {
let t = self.x^self.x.wrapping_shl(11);
self.x = self.y; self.y = self.z; self.z = self.w;
self.w ^= self.w.wrapping_shr(19)^t^t.wrapping_shr(8);
return self.w;
}

pub fn shuffle<T>(&mut self, a: &mut [T]) {
if a.len()==0 {return;}
let mut i = a.len()-1;
while i>0 {
let j = (self.rand() as usize)%(i+1);
a.swap(i,j);
i-=1;
}
}

pub fn rand_range(&mut self, a: i32, b: i32) -> i32 {
let m = (b-a+1) as u32;
return a+(self.rand()%m) as i32;
}

pub fn rand_float(&mut self) -> f64 {
(self.rand() as f64)/(<u32>::max_value() as f64)
}
}

fn main() {
let mut rng = Rand::new(0);

// Throw a dice 100 times
let v: Vec<i32> = (0..100).map(|_| rng.rand_range(1,6)).collect();
println!("{:?}",v);

// Shuffle an array
let mut v: Vec<u32> = (1..101).collect();
rng.shuffle(&mut v);
println!("{:?}",v);
}
``````

Nice attempt but you can't generate random (non-deterministic) numbers from a deterministic program

repl.it allows to read from `/dev/urandom`, so the simplest solution (and correct one) will be to read from it:

``````use std::fs::File;

fn main() {
let mut f = File::open("/dev/urandom").unwrap();
let mut buf = [0u8; 16];
println!("{:?}", buf);
}
``````

UPD: `/dev/random` -> `/dev/urandom`

10 Likes

True. This also means that a computer cannot generate a random number in any case.

That's why there is a seed in all algorithms. The current time is often used as a seed because it's "random enough".

The difference in the implementations is usually just in how well-distributed the generated numbers are, and in the performance.

If you need a few random numbers, taking the nanoseconds of the current time is good enough. If you need a lot of them in a tight loop, you could end up in the situation where the same set of numbers comes repeatedly, as the loop body always does the same thing over and over, probably in the same time.

1 Like

This is a good idea, but `/dev/random` has 2 problems you need to be aware of:
1- it's Linux (maybe Unix) only
2- you could deplete the random numbers pool, and need to wait for it to be filled again by the kernel.

`/dev/urandom` gives less-random numbers, but has no pool, so you can get all the numbers you want, knowing they could be not random at all.

I agree. The reason i pointed out is that since he is taking the seed as a parameter to a public function, if you pass the same seed you will get the same number right?

Well, if no one else is going to, I suppose I will:

``````fn get_random_number() -> i32 {
4 // chosen by fair dice roll.
// guaranteed to be random.
}
``````

(RFC 1149.5 specifies 4 as the standard IEEE-vetted random number.)

(You could also use a PCG, which is pretty simple.)

5 Likes

The seed is what defines the start of the sequence. Every time you request a new number from the RNG created with that seed you get a different one, but the sequence is always the same.

This allows you to test the RNG: By passing a pre-defined seed you can check that the sequence is always the same. You can also check with different seeds if the distribution is flat or has spikes.

Incidentally, this also allows to "reverse-check" a code: let's say you generate a certain number of codes starting from a seed. If you receive the code and you somehow know the seed used and the number of codes generated with that seed, you can know if the code belongs to that seed's sequence, therefore validating it.

By passing a semi-random number (like the current timestamp) to a RNG at runtime, you get a random sequence. As random as a computer can generate, naturally.

This thread made me have some fun thinking about how to generate randomish numbers. "Ask the user to roll a dice and type the result" was one (but I knew that RFC before, so it wasn't very creative )

I asked in another group I'm in and one of my friends recommended this method, which uses C's `rand` function:

``````fn main() {
unsafe {
srand();
println!("{}", rand());
}
}

extern "C" {
fn srand() -> u32;
fn rand() -> u32;
}
``````

I'm going to use the nanoseconds to generate time since that introduces the least amount of new concepts, but wanted to post this as another option in case anyone else runs into this issue!

1 Like

I feel compelled to opine that using C's rand is less than great on account of C's rand function being awful, and for promoting the use of `unsafe` code in cases where you really don't need it.

Oh, that reminds me of another approach: the one used by Doom back in the 90's: have a table of numbers, containing every possible `u8` value exactly once, in a randomly permuted order. The random sequence is just reading those numbers one at a time, starting from a random point. It's guaranteed to produce every possible value with a perfectly flat distribution!

1 Like

and how do you get this random point? you will need a random number for that right?

... yes...

Both `/dev/random` and `/dev/urandom` use a cryptographically secure pseudo-random number generator (PRNG) to generate the numbers. I was surprised by this -- there is a great page about it here: Myths about /dev/urandom - Thomas' Digital Garden

3 Likes

Sorry for bumping this 2 year old thread, but there's the recent fastrand if you don't need cryptographically secure random numbers. Zero dependencies as of 1.1.0. I've used it to great success here, and here's another example where it can replace rand.

1 Like