Problem with a genuine randomizer

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
Ghoti
Posts: 288
Joined: Sat Dec 31, 2005 11:06 pm

Problem with a genuine randomizer

Post by Ghoti »

Hi folks,

i now use this code to random get some numbers:

Code: Select all

// generates random number
int get_random(int lo, int hi) 
{ 
    return (rand() % (hi-lo+1)) + lo ;
} 
but this does not randomize anything... I tested my beginning of my game several times and it occured to me that i played it the same over and over again so i looked why and i saw that everytime i played the game the randomizer gave the same numbers in order everytime so i could predict the outcome of it. Why is this? is there something wrong with the code? is there better code for this?

any comments are welcome.
Garak
Posts: 46
Joined: Wed Jul 27, 2005 1:59 am

Post by Garak »

Hi,

There is probably a better solution than this, but alas this is the solution I used and it did/does work.

I created the following function:

void InitRandomNumberGen()
{
int x;
struct timeval cTime;
gettimeofday(&cTime, 0);
int seed = cTime.tv_sec % 256;
for (x=0; x < seed; x++)
rand();
}

Call this function 1 time near the start of your program. It will get the system times and modulo it to get a number between 0 and 255. Then it calls the random number generator that many times. This will ensure the first random number actually used in your program is not the same random number allways returned first by the rand() function.

As you probably figured out, the random number generator allways produces the same random numbers. Doesn't sound very random but that's the way it usually works. I think normally a seed function that uses the system time (such as the one I mention above) is included in the random number function. I was suprised the one in the PSP lib did not plant a seed based on system time for you. But if you do it yourself as described above, you will get the results you are looking for.


Garak
User avatar
Raphael
Posts: 646
Joined: Tue Jan 17, 2006 4:54 pm
Location: Germany
Contact:

Post by Raphael »

include <psputils.h> in your program and try the following

SceKernelUtilsMt19937Context ctx;
sceKernelUtilsMt19937Init(&ctx, time(NULL));

u32 rand_val = sceKernelUtilsMt19937UInt(&ctx);

the last statement should give you a random number everytime.
User avatar
Jim
Posts: 476
Joined: Sat Jul 02, 2005 10:06 pm
Location: Sydney
Contact:

Post by Jim »

->Garak. That will give 256 different start points, which is better, but not as good as calling
srand(time(NULL));
which gives RAND_MAX+1 starting points.
Jim
Garak
Posts: 46
Joined: Wed Jul 27, 2005 1:59 am

Post by Garak »

srand(time(NULL)),

Yea that's hot... that's hot. I'll be sure to update my code and use it. Thanks.

Garak
User avatar
Saotome
Posts: 182
Joined: Sat Apr 03, 2004 3:45 am

Post by Saotome »

Raphael wrote:...
SceKernelUtilsMt19937Context ctx;
sceKernelUtilsMt19937Init(&ctx, time(NULL));

u32 rand_val = sceKernelUtilsMt19937UInt(&ctx);
...
Thats even hotter, Garak ;) (Mt19937 = Mersenne twister)
It gives "better" random numbers, and it's faster (should be - didn't test on PSP - but my PS2 version is).
infj
Ghoti
Posts: 288
Joined: Sat Dec 31, 2005 11:06 pm

Post by Ghoti »

Hi folks,

If i am correct the function Raphael gave me was the best one right?
So it gives a number between? i mean i only have to get a 1 or a 2 randomized. like a boolean value :)
User avatar
Raphael
Posts: 646
Joined: Tue Jan 17, 2006 4:54 pm
Location: Germany
Contact:

Post by Raphael »

/**
* Function to return a new psuedo random number.
*
* @param ctx - Pointer to a pre-initialised context.
* @return A pseudo random number (between 0 and MAX_INT).
*/
u32 sceKernelUtilsMt19937UInt(SceKernelUtilsMt19937Context *ctx);

So the function returns a complete random value inside the u32 valuespace.
So you could just use your get_random function and replace the rand() with the mersenne twister function and it would work perfectly. Just make the context global and initialise it at the start of your program.
Or in your special case, you'd just append

rand_val = 1 + rand_val % 2;

to my code snippet.
Ghoti
Posts: 288
Joined: Sat Dec 31, 2005 11:06 pm

Post by Ghoti »

Hi,

Thanks that did the trick :) now my items come out all randomized and nice :)thank you!
Ghoti
Posts: 288
Joined: Sat Dec 31, 2005 11:06 pm

Post by Ghoti »

Hi folks,

i use:

Code: Select all

	SceKernelUtilsMt19937Context ctx;

	int RandomInit&#40;void&#41; &#123;
		sceKernelUtilsMt19937Init&#40;&ctx, time&#40;NULL&#41;&#41;;
		return 0;
	&#125;


// generates random number
	int get_random&#40;int lo, int hi&#41; 
	&#123; 
		u32 rand_val = sceKernelUtilsMt19937UInt&#40;&ctx&#41;;
		rand_val = 1 + rand_val % 2;
		return &#40;int&#41;rand_val;
	&#125; 
however rand_val is always empty :S i have sprintf the randval with %i to an char array and displayed it on screen but it was always empty. So according to that it didn't work.
however i use it in my program and for my blocks the randomness works, however when i output the get_random to a char array and display it also the ones that work on my blocks don't show anything. How can this be?
User avatar
Saotome
Posts: 182
Joined: Sat Apr 03, 2004 3:45 am

Post by Saotome »

what does empty mean?

your function:

Code: Select all

int get_random&#40;int lo, int hi&#41;
   &#123;
      u32 rand_val = sceKernelUtilsMt19937UInt&#40;&ctx&#41;;
      rand_val = 1 + rand_val % 2;
      return &#40;int&#41;rand_val;
   &#125;
generates only "1" or "2", so depends on how you're displaying them on the screen.
But I guess you won't see very much with something like a printf.
infj
Ghoti
Posts: 288
Joined: Sat Dec 31, 2005 11:06 pm

Post by Ghoti »

Well i do this to draw:

Code: Select all

int get_random&#40;int lo, int hi&#41; 
	&#123; 
		u32 rand_val = sceKernelUtilsMt19937UInt&#40;&ctx&#41;;
		rand_val = 1 + rand_val % 2;
		sprintf&#40;sBuffer, "%i", rand_val&#41;;
		drawText&#40;sBuffer&#41;;
		return &#40;int&#41;rand_val;
	&#125; 
you are right that it only gives the number between 1 en 2 but the returned value or the rand_val inside the function(see above) doesn't seem to hold any information i can use or store in an integer, what am i seeing wrong?
CyberBill
Posts: 86
Joined: Tue Jul 26, 2005 3:53 pm
Location: Redmond, WA

Post by CyberBill »

You need to be a little more specific with the way you're drawing text & what sBuffer is.

Please post the code that calls the get_random() function and we can help you figure out whats wrong, because the get_random() function looks correct to me.
Hexstr
Posts: 5
Joined: Tue May 23, 2006 5:07 am

Post by Hexstr »

Hrmm, well I guess the get_random function is correct but if you are going to use it like it is defined you may want to replace the 1 and 2 with lo and hi. Like this

Code: Select all

rand_val = lo + rand_val % hi;
Then you can just call the get_random function and pass it 1 and 2 as parameters.

Also it may not work if your context variable is not initialized. Make sure it is being initialized before the get_random function call.
BlackDiamond
Posts: 16
Joined: Sat Jul 02, 2005 7:31 pm
Location: Paris, FRANCE

Re: Problem with a genuine randomizer

Post by BlackDiamond »

Ghoti wrote:Hi folks,

i now use this code to random get some numbers:

Code: Select all

// generates random number
int get_random&#40;int lo, int hi&#41; 
&#123; 
    return &#40;rand&#40;&#41; % &#40;hi-lo+1&#41;&#41; + lo ;
&#125; 
but this does not randomize anything... I tested my beginning of my game several times and it occured to me that i played it the same over and over again so i looked why and i saw that everytime i played the game the randomizer gave the same numbers in order everytime so i could predict the outcome of it. Why is this? is there something wrong with the code? is there better code for this?

any comments are welcome.
seems you haven't initialized the generator
you have to use srand() somewhere to initialize it.

Something like that in your app should work:

Code: Select all

int main &#40;&#41;
&#123;
  /* initialize random generator */
  srand &#40; time&#40;NULL&#41; &#41;;

  /* the rest of your code here */
 ...
waterbottle
Posts: 3
Joined: Fri Jul 07, 2006 10:29 pm

Post by waterbottle »

what include is needed to use time(NULL);?

I get the error

./main.cpp: In function 'int main(int, char**)':
./main.cpp:101: error: 'time' was not declared in this scope

when I use it..
Kojima
Posts: 275
Joined: Mon Jun 26, 2006 3:49 am

Post by Kojima »

Is there a floating point random number generator in the psp sdk? (you know like on windows, where it returns between 0 and 1 and you just scale it into range yourself)
siberianstar
Posts: 70
Joined: Thu Jun 22, 2006 9:24 pm

Post by siberianstar »

dunno if there is one in the sdk,

float __randf()
{
const int max = 1024;
return (float)(rand()%max)/max;
}

very simple,
note that i've wrote it here and i didn't try it.
Kojima
Posts: 275
Joined: Mon Jun 26, 2006 3:49 am

Post by Kojima »

thanks, figured i could do it that way, just wondering if there was a fast build in func.
User avatar
Saotome
Posts: 182
Joined: Sat Apr 03, 2004 3:45 am

Post by Saotome »

you can do it like this

Code: Select all

float x;
*&#40;int*&#41;&x = &#40;rand&#40;&#41; & 0x7FFFFF&#41; | 0x3F800000;
you get a floating point number between 1.0 and 2.0 so if you need 0.0 to 1.0 you just need to subtract 1.0

this should be faster than the one with the slow division instructions ;)
return (float)(rand()%max)/max;
infj
User avatar
Raphael
Posts: 646
Joined: Tue Jan 17, 2006 4:54 pm
Location: Germany
Contact:

Post by Raphael »

Saotome wrote:you can do it like this

Code: Select all

float x;
*&#40;int*&#41;&x = &#40;rand&#40;&#41; & 0x7FFFFF&#41; | 0x3F800000;
you get a floating point number between 1.0 and 2.0 so if you need 0.0 to 1.0 you just need to subtract 1.0

this should be faster than the one with the slow division instructions ;)
return (float)(rand()%max)/max;
Nice trick :) I'll note that down in my hacky-speedy-coding-tricks note for sure. Thanks
<Don't push the river, it flows.>
http://wordpress.fx-world.org - my devblog
http://wiki.fx-world.org - VFPU documentation wiki

Alexander Berl
siberianstar
Posts: 70
Joined: Thu Jun 22, 2006 9:24 pm

Post by siberianstar »

Code: Select all

float __randf&#40;&#41;
&#123; 
 int x = &#40;&#40;&#40;&#40;rand&#40;&#41; << 8&#41; & 0x7FFFFF&#41; | 0x3F800000&#41;&#41;;
 float r;
 asm volatile&#40;
   "move %0, %1\n" &#58; "=e" &#40;r&#41; &#58; "e" &#40;x&#41;&#41;;
 return r;
&#125;
this should work.

I've made a speed test using:

Code: Select all

inline float __randf_1&#40;&#41;
&#123;
const int max = 1024; 
return &#40;float&#41;&#40;rand&#40;&#41;%max&#41;/max; 
&#125;

inline float __randf_2&#40;&#41;
&#123; 
 int x = &#40;&#40;&#40;&#40;rand&#40;&#41; << 8&#41; & 0x7FFFFF&#41; | 0x3F800000&#41;&#41;;
 float r;
 asm volatile&#40;
   "move %0, %1\n" &#58; "=e" &#40;r&#41; &#58; "e" &#40;x&#41;&#41;;
 return r;
&#125;
using a loop of 50000 cycles,
__randf_1 = 9ms
__randf_2 = 8ms
User avatar
Raphael
Posts: 646
Joined: Tue Jan 17, 2006 4:54 pm
Location: Germany
Contact:

Post by Raphael »

siberianstar wrote:

Code: Select all

float __randf&#40;&#41;
&#123; 
 int x = &#40;&#40;&#40;&#40;rand&#40;&#41; << 8&#41; & 0x7FFFFF&#41; | 0x3F800000&#41;&#41;;
 float r;
 asm volatile&#40;
   "move %0, %1\n" &#58; "=e" &#40;r&#41; &#58; "e" &#40;x&#41;&#41;;
 return r;
&#125;
this should work.

I've made a speed test using:

Code: Select all

inline float __randf_1&#40;&#41;
&#123;
const int max = 1024; 
return &#40;float&#41;&#40;rand&#40;&#41;%max&#41;/max; 
&#125;

inline float __randf_2&#40;&#41;
&#123; 
 int x = &#40;&#40;&#40;&#40;rand&#40;&#41; << 8&#41; & 0x7FFFFF&#41; | 0x3F800000&#41;&#41;;
 float r;
 asm volatile&#40;
   "move %0, %1\n" &#58; "=e" &#40;r&#41; &#58; "e" &#40;x&#41;&#41;;
 return r;
&#125;
using a loop of 50000 cycles,
__randf_1 = 9ms
__randf_2 = 8ms
Now please also bench with the Mersenne Twister ;P
<Don't push the river, it flows.>
http://wordpress.fx-world.org - my devblog
http://wiki.fx-world.org - VFPU documentation wiki

Alexander Berl
siberianstar
Posts: 70
Joined: Thu Jun 22, 2006 9:24 pm

Post by siberianstar »

using Mersenne Twister for __randf_1, __randf2:

Code: Select all

inline float __randf_1&#40;&#41;
&#123;
const int max = 1024; 
return &#40;float&#41;&#40;sceKernelUtilsMt19937UInt&#40;&ctx&#41;%max&#41;/max; 
&#125;

inline float __randf_2&#40;&#41;
&#123; 
 int x = &#40;&#40;&#40;&#40;sceKernelUtilsMt19937UInt&#40;&ctx&#41; << 8&#41; & 0x7FFFFF&#41; | 0x3F800000&#41;&#41;;
 float r;
 asm volatile&#40;
   "move %0, %1\n" &#58; "=e" &#40;r&#41; &#58; "e" &#40;x&#41;&#41;;
 return r;
&#125;

__randf_1 = 42ms
__randf_2 = 40ms
User avatar
Raphael
Posts: 646
Joined: Tue Jan 17, 2006 4:54 pm
Location: Germany
Contact:

Post by Raphael »

siberianstar wrote:using Mersenne Twister for __randf_1, __randf2:

__randf_1 = 42ms
__randf_2 = 40ms
Du'h that's surprising, thought it wouldn't be that much slower.
<Don't push the river, it flows.>
http://wordpress.fx-world.org - my devblog
http://wiki.fx-world.org - VFPU documentation wiki

Alexander Berl
siberianstar
Posts: 70
Joined: Thu Jun 22, 2006 9:24 pm

Post by siberianstar »

yep, srand is much faster
User avatar
Jim
Posts: 476
Joined: Sat Jul 02, 2005 10:06 pm
Location: Sydney
Contact:

Post by Jim »

Every 624 calls to mtrand, it has to go off and update the RNG's state, which is 624 32bit integers. The code for doing a single mtrandom number is normally just a handful of xors, shifts and ands.

The C99 standard recommends this implementation for rand()

Code: Select all

static unsigned long int next = 1;
int rand&#40;void&#41; // RAND_MAX assumed to be 32767
&#123;
next = next * 1103515245 + 12345;
return &#40;unsigned int&#41;&#40;next/65536&#41; % 32768;
&#125;
which is just one mul, one shift, one add and one and. Many C runtimes use this exact algorithm.

So what you'll find is mtrand performance will be 'lumpy' compared with rand. Do you really need mtrand? You'd traditionally want that only if you were doing specific statistical analysis.

Jim
Last edited by Jim on Sat Aug 12, 2006 11:24 am, edited 1 time in total.
User avatar
Raphael
Posts: 646
Joined: Tue Jan 17, 2006 4:54 pm
Location: Germany
Contact:

Post by Raphael »

Jim wrote:Every 624 calls to mtrand, it has to go off and update the rng's state, which is 624 32bit integers. The code for doing a single mtrandom number is normally just a handful of xors, shifts and ands.

The C99 standard recommends this implementation for rand()

Code: Select all

static unsigned long int next = 1;
int rand&#40;void&#41; // RAND_MAX assumed to be 32767
&#123;
next = next * 1103515245 + 12345;
return &#40;unsigned int&#41;&#40;next/65536&#41; % 32768;
&#125;
which is just one mul, one shift and one and. Many C runtimes use this exactly algorithm.

So what you'll find is mtrand performance will be 'lumpy' compared with rand. Do you really need mtrand? You'd traditionally want that only if you were doing specific statistical analysys.

Jim
Reasonable. I doubt that this implementation has a near-equal distribution over the int range (won't prove that mathematically though here :P). So if you use it for something where every random number should have same probability, it's bad (dice anyone?).
<Don't push the river, it flows.>
http://wordpress.fx-world.org - my devblog
http://wiki.fx-world.org - VFPU documentation wiki

Alexander Berl
Post Reply