1
int main(void)
{
    srand(time(NULL));
    char name[10][100] = {
      "ali",
      "abu",
      "kamil",
      "sarah",
      "siti",
      "aina",
      "ain",
      "hafiz",
      "sofea",
      "amil"
    };
    int c, n;

    for (c = 1; c <= 5; c++) {

      n = rand() % (10 - 1) + 1;
      printf("%s\n", name[n]);

    }

    return 0;
}

Whenever I run it, there are 2 names that come out the same. My lecturer said that I need to add an extra code to avoid redundant random numbers, but I don't know how. Can you explain?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 5
    For one thing the random selection should be `n = rand() % 10; ` to give numbers in the range 0 to 9. If you want unique names there are many other questions here about that. One simple way to deal with it is to have a status array which you use to show which names have already been used, and you keep trying until an unused one is found. Another way is to populate an `int` array with the numbers 0 to 9 and swap them around at random, finally using the first 5 entries to print. – Weather Vane Apr 22 '17 at 13:27
  • See http://stackoverflow.com/a/42790214/4386427 – Support Ukraine Apr 22 '17 at 13:48
  • what's the desired output ? –  Apr 22 '17 at 13:48
  • Here is another [previous answer](http://stackoverflow.com/a/27867571/4142924) which generates a unique list of random numbers, which you could use to index the array of names. – Weather Vane Apr 22 '17 at 14:07
  • Search for memoization, i. e. storing (the indexes) of the already printed names and perhaps checking the randomly generated number before using it. – Ziezi Apr 22 '17 at 14:09

3 Answers3

0

Your pulling the names randomly with replacement, meaning the selected names remain in the list.

If the desired result is to show each name once and only once then remove the name from the set once it's been randomly selected.

Glenn Ferrie
  • 10,290
  • 3
  • 42
  • 73
0

I thought something like:

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

int main(void)
{
    unsigned char nameflgs[10] = {0};
    srand(time(NULL));
    char name[10][100] = {
      "ali",
      "abu",
      "kamil",
      "sarah",
      "siti",
      "aina",
      "ain",
      "hafiz",
      "sofea",
      "amil"
    };
    int c, n;

    for (c = 1; c <= 5; c++) 
    {
        for (int l = 0;l < 10;l++)
        {
            n = rand() % 10;
            if (!nameflgs[n])
            {
                printf("%s\n", name[n]);
                nameflgs[n] = 1;
                break;
            }
        }
    }

    return 0;
}

Going on as I understand the initial problem Cody.... The problem is to print 5 random names from a list without repeating names. I.e. you get a set of names. So I've added a 'namesflgs' array of 10 (initially zero) elements that acts as 'flags' where 0 = name not selected and 1 = name selected. The two major changes are the inner for loop on variable 'l' and the if statement inside. In the inner for loop n is randomly set a number from 0-9. The following if statement then tests our nameflgs array to see if it has already been output. If not the name is output and the array is updated to reflect this, then a break is executed to break out of the inner for loop. Next name. Once again our inner for loop is entered, n is assigned. If however n gets a number via our nameflgs array that has already been used (tested by our if statement) then the inner body of the if is skipped and we go back to our inner for loop to try assigning another n. Now if l > 9 tries the loop exits (to prevent a infinite loop occurring) and if c > 5 then that for loop exits and the program eventually terminates. However if the original OP wanted all 10 names (in a random fashion) outputting I suppose I could've added a second if to break from the outer for loop early (for efficiency reasons if l > 10.)

Edit

However having said that I think I tend to prefer this second thought:

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

int main(void)
{
    srand(time(NULL));
    char name[10][100] = {
      "ali",
      "abu",
      "kamil",
      "sarah",
      "siti",
      "aina",
      "ain",
      "hafiz",
      "sofea",
      "amil"
    };

    int nameindxs[10];

    //Initialize nameindxs from 0 to 9
    for (int n = 0;n < 10;n++)
        nameindxs[n] = n;

    //Randomly swap nameindxs array going through twice
    for (int l = 0;l < 10 * 2;l++)
    {
        int indx1 = rand() % 10;
        int indx2 = rand() % 10;
        int tmp = nameindxs[indx1];
        nameindxs[indx1] = nameindxs[indx2];
        nameindxs[indx2] = tmp;
    }

    //Output names using nameindxs array
    for (int c = 0; c < 5; c++) 
    {
        printf("%s\n", name[nameindxs[c]]);
    }

    return 0;
}

This time initialize an array of 10 numbers from 0-9. Then go through the array a couple of times randomly swapping the numbers. We should be left with a fairly random (unique) set. Then finally just output as many names as we need using our nameindxs as an index into the names array.

foxcode64
  • 151
  • 3
0

You may be making this harder than it needs to be. One easy way to randomize the strings is simply to declare an array-of-pointers rather than a 2D array. This allows you to use a simple 'shuffle', like a Fisher-Yates Shuffle to randomize the array of pointers to reorder the strings. This guarantees you will never encounter a duplicate string and you are free to re-shuffle as many times as needed to change the order over-and-over again.

There are a number of ways to implement the shuffle. You can implement a semi-generic shuffle that takes an array of pointers as an arguments and simply shuffle the pointers, or you can maintain a second integer array of indexes for your main array and shuffle the strings using the shuffled integers as the indexes for your array of pointers (or 2D array).

The following just implements the shuffle of pointers directly. If you must use a 2D array to hold the strings, instead of an array-of-pointers, then just use a separate array. In either case, if you need to output some subset of the strings, just output the first X-number of strings from the shuffled array (or using the shuffled indexes). For example:

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

void ptr_shuffle (void *arr, size_t n);

int main (void) {

    char *name[] = {"ali",  /* array of pointers */
                    "abu",
                    "kamil",
                    "sarah",
                    "siti",
                    "aina",
                    "ain",
                    "hafiz",
                    "sofea",
                    "amil"};
    size_t n = sizeof name / sizeof *name;

    srand (time (NULL));  /* initialize psuedo-random sequence */

    for (size_t i = 0; i < n; i++)  /* print original array */
        printf ("name[%zu] : %s\n", i, name[i]);

    ptr_shuffle (name, n); /* shuffle 'n' pointers in 'name */

    printf ("\nshuffled:\n");
    for (size_t i = 0; i < n; i++)  /* print shuffled array */
        printf ("name[%zu] : %s\n", i, name[i]);

    return 0;
}

/* shuffle an array of pointers */
void ptr_shuffle (void *arr, size_t n)
{
    char *tmp, **a = (char **)arr;
    size_t i;

    while (n-- > 1) {
        i = rand() % (n + 1);
        tmp  = a[i];
        a[i] = a[n];
        a[n] = tmp;
    }
}

Example Use/Output

$ ./bin/array_ptr_shuffle
name[0] : ali
name[1] : abu
name[2] : kamil
name[3] : sarah
name[4] : siti
name[5] : aina
name[6] : ain
name[7] : hafiz
name[8] : sofea
name[9] : amil

shuffled:
name[0] : siti
name[1] : kamil
name[2] : ali
name[3] : abu
name[4] : aina
name[5] : amil
name[6] : sofea
name[7] : sarah
name[8] : ain
name[9] : hafiz

Look things over and let me know if you have further questions.


Shuffling Questions & Answers

Continuing from your comment. If you have a set of anything which you want to shuffle and maintain the association between all objects in a set, you should be thinking struct (a structure). You can treat an array-of-pointers-to-struct the same way you treat an array-of-pointers-to-anything. Recall on any system, a pointer-is-a-pointer-is-a-pointer. You can handle pointers generically regardless what they point to. (yes, it is up to you to keep what they point to straight, but dealing with the pointers is the same).

Below, to avoid allocating for each member of each structure, and avoiding using a fixed width array for each, I use a compound literal to create static storage for each question/answer set and then just assign the address to the same name array. You are free to provide whatever type storage and initialization you like. Obviously, if you are reading the questions and answers from a file, you will need to allocate storage in a different way.

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

typedef struct qa {
    char *q, *a1, *a2, *a3;
} qa;

void ptr_shuffle (void *arr, size_t n);

int main (void) {

                    /* using compound literalts to assign address to name */
    qa *name[] = {  &(struct qa){ "What is feline?", "dog", "cat", "mouse" },
                    &(struct qa){ "What is ice?", "Hydrogen", "H20", "water" },
                    &(struct qa){ "What is k9?", "monkey", "dog", "frog" },
                    &(struct qa){ "Must I malloc ptrs?", "yes", "no", "depends" }
                 };
    size_t n = sizeof name / sizeof *name;

    srand (time (NULL));  /* initialize psuedo-random sequence */

    for (size_t i = 0; i < n; i++)  /* print original array */
        printf ("%s\n\ta) %s\n\tb) %s\n\tc) %s\n",
                name[i]->q, name[i]->a1, name[i]->a2, name[i]->a3);

    ptr_shuffle (name, n); /* shuffle 'n' pointers in 'name */

    printf ("\nshuffled:\n");
    for (size_t i = 0; i < n; i++)  /* print shuffled array */
        printf ("%s\n\ta) %s\n\tb) %s\n\tc) %s\n",
                name[i]->q, name[i]->a1, name[i]->a2, name[i]->a3);

    return 0;
}

/* shuffle an array of pointers */
void ptr_shuffle (void *arr, size_t n)
{
    char *tmp, **a = (char **)arr;
    size_t i;

    while (n-- > 1) {
        i = rand() % (n + 1);
        tmp  = a[i];
        a[i] = a[n];
        a[n] = tmp;
    }
}

Example Use/Output

$ ./bin/array_ptr_shuffle_struct
What is feline?
        a) dog
        b) cat
        c) mouse
What is ice?
        a) Hydrogen
        b) H20
        c) water
What is k9?
        a) monkey
        b) dog
        c) frog
Must I malloc ptrs?
        a) yes
        b) no
        c) depends

shuffled:
Must I malloc ptrs?
        a) yes
        b) no
        c) depends
What is k9?
        a) monkey
        b) dog
        c) frog
What is feline?
        a) dog
        b) cat
        c) mouse
What is ice?
        a) Hydrogen
        b) H20
        c) water

Look that over and drop a comment if you have questions. There are many good questions/answers on this site that cover compound literals.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • but what if i want to do like a quiz. if i shuffle the question, how i make sure that it will display the correct answer? – sollehah othman Apr 24 '17 at 02:52
  • Then you want to use an *array-of-struct* (which contains both the question and a set of answers) and pass a pointer to the array of struct to `shuffle`. Then the order of the questions will be randomized, but will remain associated with the proper answer sets. – David C. Rankin Apr 24 '17 at 04:32
  • now I understand. but what if the user enters 'D' which is not a part of the question? what condition should i do? i tried to do like this: while(ans!= "a"|| ans!= "A"&& ans!= "b"||ans!= "B"&&ans!= "c"||ans!= "C") but it keeps asking me to re enter even i enter a/b/c – sollehah othman Apr 26 '17 at 15:50
  • You can simply use a flag (e.g, `int ans = 0;`) and a `switch` statement to pick off `case 'A':` - `case 'C`:` (where you set `ans = 1;` and provide any *correct/incorrect* output and then `default: fprintf (stderr, "error: invalid selection.\n");`. You wrap it all in a `while (ans == 0) { switch (choice) { case A: ... ans = 1; break; ... default: ... } }` (all of this could be in a `check_answer` function, which you would drive for each question with `for (size_t i = 0; i < n; i++) { ... check_answer (name[i], i); ... }` – David C. Rankin Apr 26 '17 at 17:19