News:

Download Pelles C here: http://www.smorgasbordet.com/pellesc/

Main Menu

rand()%4

Started by dpanteli, April 20, 2012, 07:29:30 PM

Previous topic - Next topic

dpanteli

Hello,
Please can you help me? Why the following prints always 3?

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

int main (void)
  {
   int i;
      srand((unsigned) time(NULL));
      i = rand() %4;

   printf("%d\n",i);

      return 0 ;
  }

Thank you in advanced
Dimitris Pantelios

czerny

rand() does not return real random numbers. It returns pseudo random numbers. They produce a sequence of numbers which look random. But the sequence as always identical if the start value of the sequence is identical. So if you want to produce different values, you should set the start value to something different with the function srand(). You can use time() to randomize this a little.

czerny

CommonTater

Quote from: dpanteli on April 20, 2012, 07:29:30 PM
Hello,
Please can you help me? Why the following prints always 3?

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

int main (void)
  {
   int i;
      srand((unsigned) time(NULL));
      i = rand() %4;

   printf("%d\n",i);

      return 0 ;
  }

Thank you in advanced
Dimitris Pantelios

Make a loop and tell it to print 20 or 30 numbers... see if the sequence is always the same.  It should not be.

Also, with such a small range (4) it's entirely possible the sequences generated from the seed (srand() ) by coincidence have been starting with 3.

You don't need the unsigned cast in your seed, srand() always builds sequences from 0 to RAND_MAX.




Bitbeisser

#3
Quote from: czerny on April 20, 2012, 08:28:16 PM
rand() does not return real random numbers. It returns pseudo random numbers. They produce a sequence of numbers which look random. But the sequence as always identical if the start value of the sequence is identical. So if you want to produce different values, you should set the start value to something different with the function srand(). You can use time() to randomize this a little.
That's what he already tried....
Quote from: CommonTaterAlso, with such a small range (4) it's entirely possible the sequences generated from the seed (srand() ) by coincidence have been starting with 3.
That seems indeed to be the case. Just change the range to 5 (% 5) and you will see that the results will kind of alternate between 2 and 3...

Ralf

dpanteli

Yes, the sequences are different... However the strange is that the first number (as many times I tried) was always the same... Maybe by coincidence...
Although, when I tried to use rand()%3 then the first number of the sequenses are not always the same... Strange behaviour...

CommonTater

#5
Quote from: dpanteli on April 21, 2012, 09:46:22 AM
Yes, the sequences are different... However the strange is that the first number (as many times I tried) was always the same... Maybe by coincidence...
Although, when I tried to use rand()%3 then the first number of the sequenses are not always the same... Strange behaviour...

Try it with rand() % 5000 and see what the first number is...  I still think it's the very small range you're using.

Don't forget these are not true random numbers.  This is something computers don't do well. they are sequences of pseudo-random numbers generated by the srand() function.

You might try something like this to enhance the apparent randomness of small ranges...


srand(time(NULL));  // seed random number generator
rand();                 // discard first value

x = (rand() % 156) % 4;   //  start from a larger range


... some experimentation will help you find the best way for your own code.


Vortex

Hi dpanteli,

You can use the Windows timer to support the random number generator.
Code it... That's all...

CommonTater

Quote from: Vortex on April 21, 2012, 07:26:52 PM
Hi dpanteli,

You can use the Windows timer to support the random number generator.

As in ...  srand(GetTickCount());     

That's a good one too.

ngohungcuong

I think it is error of srand and rand function. I test this code
srand(GetTickCount());
rand()%4;
it alway return same result.

ngohungcuong

#9
i test below code

#include <windows.h>
#include <stdint.h>

void entry(void)
{
int t;
int i;
char szTg[100];
uint32_t count[4];
count[0] = 0;
count[1] = 0;
count[2] = 0;
count[3] = 0;

for (i = 0; i < 1000; ++i) {
srand(GetTickCount());
t = rand() % 4;
++count[t];
Sleep(1);
}
wsprintf(szTg, "%d %d %d %d", count[0], count[1], count[2], count[3]);
MessageBox(NULL, szTg, NULL, MB_OK);
ExitProcess(0);
}

then the result : count[3] is 1000

ngohungcuong

then i add msvcrt.lib to linker tab cause linker link to msvcrt.dll
program work fine


TimoVJL

#11
It seem that in this case srand() in loop don't work well as it starts sequence again:(
A modified example to see generated rand() values:
#include <stdint.h>
#include <stdlib.h>
#pragma comment(lib, "user32")
//#pragma comment(lib, "msvcrt")
//#pragma comment(linker, "-subsystem:console")

int __stdcall GetTickCount(void);
void __stdcall Sleep(int);
uint32_t count[4];
//void __cdecl mainCRTStartup(void)
void main(void)
{
for (int i = 0; i < 60; ++i) {
int t,idx;
t = rand();
idx = t % 4;
++count[idx];
printf("%2d %2d %2d %2d %2d %d\n", count[0], count[1], count[2], count[3], idx, t);
Sleep(1);
}
return;
}

Also with t = rand()/4;
idx = t % 4;
  PellesC gives more random numbers for that scale.
May the source be with you

ika

#12
You can just use the Microsoft Crypto API's instead:


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

int __cdecl main(void){

HCRYPTPROV cp;
int tgt;

(void)CryptAcquireContextA(&cp,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT);

for(int i = 0; i < 30; i++){
CryptGenRandom(cp,sizeof(int),(void*)&tgt);
printf("%d\n",abs(tgt % 4));
}

(void)CryptReleaseContext(cp,0);

return EXIT_SUCCESS;
}


This seems to work for me, I can get some pretty random numbers using it at this range.

TimoVJL

Results: 1.08 - 2.83
#include <stdio.h>
#include <stdlib.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincrypt.h>

unsigned long count[4];

int __cdecl main(void){

HCRYPTPROV cp;
int tgt;

(void)CryptAcquireContextA(&cp,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT);

for(int i = 0; i < 100; i++){
CryptGenRandom(cp,sizeof(int),(void*)&tgt);
int idx = abs(tgt % 4);
++count[idx];
//printf("%2d %2d %2d %2d %2d %d\n", count[0], count[1], count[2], count[3], idx, tgt);
}

(void)CryptReleaseContext(cp,0);
printf(" 1  2  3  4\n%2d %2d %2d %2d\n", count[0], count[1], count[2], count[3]);
int min=100, max=0;
for (int i=0; i<4; i++) {
if (count[i] < min) min = count[i];
if (count[i] > max) max = count[i];
}
printf("%0.2f %d %d\n", (double)max/min, min, max);
return EXIT_SUCCESS;
}
rand() based gives 1.08
May the source be with you

ika

Here is the same code, again, but with the new crypto API. When I looked at MSDN it says that the old one has been deprecated...
Maybe not meaningful today, but their new API is pretty similar if it's just to generate some numbers. It even has a special setting for that.

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

#pragma comment(lib,"bcrypt.lib")

int __cdecl main(void){

BCRYPT_ALG_HANDLE bah;
int tgt;

BCryptOpenAlgorithmProvider(&bah,BCRYPT_RNG_ALGORITHM,NULL,0);

for(int i = 0; i < 30; i++){
BCryptGenRandom(bah,(void*)&tgt,sizeof(int),0);
printf("%d\n",abs(tgt) % 4);
}

BCryptCloseAlgorithmProvider(bah,0);

return EXIT_SUCCESS;
}