0

I'm currently making a quiz app. When a user starts the quiz random questions show up like you would expect from a quiz app. The problem is, it is not quite random. It does show random questions, but the questions repeat. I wanted to make sure they do not repeat until the end! My code is :

int Questions = arc4random_uniform(142);
switch (Questions) {
    case 0:

        break;

    case 1:
        break;

(...)

Isn't there a better way to do it? A way to just not repeat the questions? Thank you so much!

Richard Stelling
  • 25,607
  • 27
  • 108
  • 188
Mykod
  • 587
  • 1
  • 5
  • 16
  • 1
    "true" randomness implies there must be repetition with a certain probability. If you don't want the questions to repeat you don't want them to be random. So the question is, in what exact way should the questions be non-random? i.e. should the questions never be repeated, or is it enough if they are not repeated within a block of 50, or ..? – Joni Nov 09 '13 at 19:32
  • See [Random integer loop](http://stackoverflow.com/questions/19768714/random-integer-loop/19768854#19768854) for a similar question. – godel9 Nov 09 '13 at 19:32
  • possible duplicate of [To generate array of random numbers in a given range in "C"](http://stackoverflow.com/questions/4173610/to-generate-array-of-random-numbers-in-a-given-range-in-c) – Martin R Nov 09 '13 at 19:35
  • Would you please explain why you think (expressed in another question) that this question has not been answered? You asked how to get a set of things in random order without duplicates. You have a couple of answers with working code that both do exactly that. – pjs Nov 11 '13 at 18:00

4 Answers4

2

A shuffle may be your best solution:

// Setup
int questionCount = 10; // real number of questions
NSMutableArray *questionIndices = [NSMutableArray array];
for (int i = 0; i < questionCount; i++) {
    [questionIndices addObject:@(i)];
}
// shuffle
for (int i = questionCount - 1; i > 0; --i) {
    [questionIndices exchangeObjectAtIndex: i
        withObjectAtIndex: arc4random_uniform((uint32_t)i + 1)];
}
// Simulate asking all questions
for (int i = 0; i < questionCount; i++) {
    NSLog(@"questionIndex: %i", [questionIndices[i] intValue]);
}


NSLog output:
questionIndex: 6
questionIndex: 2
questionIndex: 4
questionIndex: 8
questionIndex: 3
questionIndex: 0
questionIndex: 1
questionIndex: 9
questionIndex: 7
questionIndex: 5

ADDENDUM

Example with actual text being printed after shuffling

// Setup
NSMutableArray *question = [NSMutableArray arrayWithObjects:
    @"Q0 text", @"Q1 text", @"Q2 text", @"Q3 text", @"Q4 text",
    @"Q5 text", @"Q6 text", @"Q7 text", @"Q8 text", @"Q9 text", nil];
// shuffle
for (int i = (int)[question count] - 1; i > 0; --i) {
    [question exchangeObjectAtIndex: i
        withObjectAtIndex: arc4random_uniform((uint32_t)i + 1)];
}
// Simulate asking all questions
for (int i = 0; i < [question count]; i++) {
    printf("%s\n", [question[i] UTF8String]);
}

Sample output:
Q9 text
Q5 text
Q6 text
Q4 text
Q1 text
Q8 text
Q3 text
Q0 text
Q7 text
Q2 text
pjs
  • 18,696
  • 4
  • 27
  • 56
  • thank you! But my question is, i just add your code and then I can use this method: int questionIndices = arc4random_uniform((uint32_t)i); switch (questionIndices) { case 0: break; case 1: break; to add the questions? Thank's! – Mykod Nov 09 '13 at 22:40
  • @Mykod No, you don't want to do that. The calls to random have already been made in the shuffle. In my final loop I'm logging the question index. You should replace the `NSLog` command with commands to print the question which has that index. – pjs Nov 09 '13 at 22:46
  • so I do : for (int i = 0; i < questionCount; i++) { NSLog(@"question1"); } for (int i = 1; i < questionCount; i++) { NSLog(@"question2", [questionIndices[i] intValue]); } (question 1 and question 2 are the questions itself)? – Mykod Nov 09 '13 at 22:51
  • @Mykod Hopefully the addendum will clarify things for you. Replace "Q* text" with the actual text of your questions. – pjs Nov 09 '13 at 23:32
  • 1
    ohhh, now I get it, there is just one minor problem, when the questions appear, I also have to show the possible answers and I say if the right button is hidden or not, how can I make add them to the NSArray? I have it like : question.text = (...) answer.text= (...) Right3.hidden=No; Thanks! – Mykod Nov 10 '13 at 08:23
  • @Mykod If it were my project, I'd make Question objects which have all the attributes you need - the question text, the answer/response text(s), an identifier for the correct answer, etc. But that's an entirely different question than how do you get all of the questions in random order without dups. – pjs Nov 10 '13 at 17:18
1

The idea is to use each question once until all questions have been used.

Sample code. Note that the questionIndex does not repeat.

// Setup
int questionCount = 10; // real number of questions
NSMutableArray *questionIndexes = [NSMutableArray array];
for (int i=0; i<questionCount; i++)
    [questionIndexes addObject:@(i)];

// Simulate asking all questions
while (questionIndexes.count) {
    // For each round 
    unsigned long arrayIndex = arc4random_uniform((uint32_t)questionIndexes.count);
    int questionIndex = [questionIndexes[arrayIndex] intValue];
    [questionIndexes removeObjectAtIndex:arrayIndex];
    NSLog(@"arrayIndex: %lu, questionIndex: %i", arrayIndex, questionIndex);
}

NSLog output:
arrayIndex: 9, questionIndex: 9
arrayIndex: 5, questionIndex: 5
arrayIndex: 5, questionIndex: 6
arrayIndex: 3, questionIndex: 3
arrayIndex: 3, questionIndex: 4
arrayIndex: 4, questionIndex: 8
arrayIndex: 2, questionIndex: 2
arrayIndex: 0, questionIndex: 0
arrayIndex: 1, questionIndex: 7
arrayIndex: 0, questionIndex: 1

zaph
  • 111,848
  • 21
  • 189
  • 228
  • thank you!! so I just add that code and then I may still use this method? switch (questionCount) { case 0: break; case 1: break; – Mykod Nov 09 '13 at 22:11
0

Any random generator is actually pseudorandom. By default it is started from the same initial value. To make it it "real random" you should supply unique start value i.e. "salt" for each run. As a simplest approach you can use [NSDate timeIntervalSinceReferenceDate].

  • 3
    `arc4random_uniform()` does not need to be seeded, as far as I know. – Martin R Nov 09 '13 at 19:32
  • `arc4random` is known as a "cryptographic pseudo-random number". It is re-seeded from the kernel random number subsystem on a regular basis – zaph Nov 09 '13 at 20:16
0

Put your questions in an array and put the random number in the objectWithIndex method of NSMutableArray. Then remove the question from the array. Whenever a index is chosen, but there is not a question anymore, try it again.

yoeriboven
  • 3,541
  • 3
  • 25
  • 40
  • @Tony Oops, correct. For the index use: arc4random_uniform(array.count) that way there will not be any re-tries. – Zaph 22 mins ago – zaph Nov 09 '13 at 20:36
  • I'm sorry, but could you please give me a more detailed explanation? Thank you! – Mykod Nov 09 '13 at 20:37