1

So I'm just learning C and I would like to know how you could prevent a variable randomized with the rand() function from repeating the same number. I have a script which simply randomizes and prints a variable in a for loop 4 times. How could I make it so the variable never gets the same number after each time it uses the rand() function?

#include <stdio.h>
#include <stdlib.h>
int randomInt;
int main()
{
    srand(time(0));
    for (int i = 0; i < 4; ++i) {
        randomInt = rand() % 4;
        printf("%d\n", randomInt);
    }
    return 0;
}
Akaneth
  • 11
  • 2
  • 2
    I am unsure what you are actually asking here. Are you trying to ensure that you never get the same number twice (like dealing from a shuffled deck of cards, once a card has been dealt, it is no longer in the deck) or do you just never want the same number back-to-back? Or is it that you want a different seed each time you run the program so you don't always get the same sequence of psuedo-random numbers each time you run? – Christian Gibbons Feb 02 '18 at 21:09
  • I never want to see the same number twice, I want to make sure every number is only printed out once in a random order. – Akaneth Feb 02 '18 at 22:20
  • 1
    Then you don't want random numbers. You want a specific set of numbers, in random order. Create an array of those numbers, then shuffle it (Google "Fisher-Yates") – Lee Daniel Crocker Feb 02 '18 at 23:44

5 Answers5

3

On most machines, int is 32 bits. So after 232 iterations, you are sure that you'll get some repetition (and probably much before).

If you restrict yourself to much less loops, consider e.g. keeping an array of previously met random numbers (or some hash table, or some binary tree, or some other container).

For a loop repeated only 4 times, keeping an array of (at most 4-1) previously emitted numbers is quite simple, and efficient enough.

Read also about the pigeonhole principle.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • `rand()` is a [linear congruential generator](https://en.wikipedia.org/wiki/Linear_congruential_generator), so depending on the implementation, the sequence of numbers is usually 2^31 or 2^31-1 long, after which it will repeat the exact same sequence again. The sequence in all implementations does not contain repeat integers. – Patrick Roberts Feb 02 '18 at 21:43
3

A slightly different approach.

int set[] = {0, 1, 2, 3 } ;
srand(time(0));
shuffle(set,4);

using the shuffle algorithm given in this question https://stackoverflow.com/a/6127606/9288531

Geek Wisdom
  • 306
  • 1
  • 4
0

What you could do is keeping track of each number you already generated.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int hasMyNumberAlreadyBeenGenerated(int number, int generatedNumbers[], int size){

  for(int i = 0; i < size + 1; i++){
    //If you already generated the number, it should be present somewhere in your array
    if(generatedNumbers[i] == number) return 1;
    //If you did not, find the first available space in your array, and put the number you generated into that space
    if(generatedNumbers[i] == 0){
      generatedNumbers[i] = number;
      break; //No need to continue to check the array
    }
  }

  return 0;
}


int main()
{
  int randomInt;
  int generatedNumbers[4];
  //We set "0" in all the array, to be sure that the array doesn't contain unknown datas when we create it
  memset(generatedNumbers, 0x0, sizeof(generatedNumbers));

    srand(time(0));
    for (int i = 0; i < 4; ++i) {
        randomInt = rand() % 4 + 1;
        //As long as the number you generate has already been generated, generate a new one
        while(hasMyNumberAlreadyBeenGenerated(randomInt, generatedNumbers, i) == 1){
          randomInt = rand() % 4 + 1;
        }
        printf("generated : %d\n", randomInt);
    }
    return 0;
}

The problem with this method is that you can't generate a 0, because if you do you'll endlessly loop. You can bypass this problem using a dynamic array using malloc() function. If you want to write clean code you should define how many numbers you want to generate with a #define.

Nark
  • 454
  • 1
  • 7
  • 18
0

I'm guessing that you are getting the same numbers because your are running your program multiple times within the same second. If time(0) hasn't changed, you will have the same seed and the same random numbers generated. Unless your program runs extremely quickly, I imagine using a seed based on microseconds instead of seconds would work:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/time.h>
    int randomInt;
    int main()
    {
        struct timeval my_microtimer;
        gettimeofday(&t1, NULL);
        srand(t1.tv_sec * my_microtimer.tv_usec);
        for (int i = 0; i < 4; ++i) {
            randomInt = rand() % 4;
            printf("%d\n", randomInt);
        }
        return 0;
    }
JCollier
  • 1,102
  • 2
  • 19
  • 31
0

What you seem to be asking is a non-random set of numbers 0 to 3 in a random order. Given that;

int set[] = {0, 1, 2, 3 } ;
int remaining = sizeof(set) / sizeof(*set) ;
while( remaining != 0 )
{
    int index = rand() %  sizeof(set) / sizeof(*set) ;
    if( set[index] > 0 )
    {
        printf( "%d\n", set[index] ) ;
        set[index] = -1 ;
        remaining-- ;
    }
}  

For very large sets, this approach may not be practical - the number of iterations necessary to exhaust the set is non-deterministic.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • 1
    Make the modulus (your hardcoded `4`) equal to `remaining`, and instead of assigning the value in the chosen `index` to `-1`, swap it with the value at `--remaining`. – Patrick Roberts Feb 02 '18 at 21:48
  • @PatrickRoberts : Modified, but not as you suggested; the magic number was an error. Your proposal would be a substantially different solution, and you should perhaps post it in your own right. However if I understand your proposal correctly, modifying the set will increase the population of certain values and therefore the likelihood of said values being selected - it might therefore be less random. – Clifford Feb 03 '18 at 09:34
  • I think you misunderstood then. All it does is allow the unique set of numbers to be printed in random order in exactly `sizeof(set)/sizeof(*set)` iterations. – Patrick Roberts Feb 03 '18 at 19:29
  • I may indeed have misunderstood. All the more reason for you to post a solution. It would be a useful contribution and open to constructive criticism. I know this is not the best possible solution - I was aiming for _short_. My point is that if you increase the population of a specific value, you increase the chance of that value being selected next, so the sequence would be biased by whatever value was selected first. If you beleive I am wrong - and I may be because I cannot see your solution, only a _description_ of it. – Clifford Feb 04 '18 at 17:59
  • It's just a Fisher-Yates shuffle. – Patrick Roberts Feb 04 '18 at 19:35
  • @PatrickRoberts : You are not listening! I understand that what is required is a shuffle, not a random number sequence; that is stated in the answer. And I understand that this is a suboptimal solution, also stated. But I am not about to change _my_ answer to be _your_ answer. To do so would be academically dishonest. I cannot understand why you would choose to lobby for changing this answer rather than posting your own. You have spent more time arguing about it than it would have taken to just do it. – Clifford Feb 05 '18 at 07:47
  • If you re-read my last 2 comments, I haven't lobbied for you to change your answer, I'm just clarifying why mine isn't `less random` or `biased`. – Patrick Roberts Feb 05 '18 at 07:53
  • @PatrickRoberts ... and I have conceded that may be so, and pointed out that comments are not a good means if explaining that. If you are not posting an answer, we should refrain from discussing it further. Posting what seems likely is a superior answer is the right thing to do for the OP, the SO community and to clarify my apparent misunderstanding. – Clifford Feb 05 '18 at 08:18