0

For a mini project I am making a quiz program My current (relavant) code is as follows:

static Random _r = new Random();        
static int Quiz()
{
    string[,] QAndA = {
        {"What is the capital of France", "Paris"},
        {"What is the capital of Spain", "Madrid"},
                ...
        {"What is the captial of Russia", "Moscow"},
        {"What is the capital of Ukraine", "Kiev"},
    };

    for (int i = 0; i < NUM_QUESTIONS; i++)
    {
        int num = _r.Next(QAndA.GetLength(0) / 2);
        Question(QAndA[num, 0], QAndA[num, 1]);
    }
}

Now, the obvious problem with this is that the random numbers can be repeated, meaning that questions can be repeated.

Now, my teacher (yes, this is a school thing) told me to look for shuffling algorithms, but I have failed to find any that work for multidimensional arrays like i have used.

I am a fairly new c# programmer, but I have experience with c++ and the program is a commandline program (at the moment :) ), if that matters/helps

So, the question is, what's the best way of reordering/shuffling the multidimensional array to be in a random order?

1
  • 3
    Unrelated to your question I would suggest creating a Question class with a Question and an Answer property. Commented Oct 14, 2011 at 12:11

5 Answers 5

4

You are looking at the wrong problem. Instead of a multidimensional array (something quite rarely used because scarcely supported) use a jagged array.

string[][] questions = new[] { 
    new [] {"What is the capital of France", "Paris"}, 
    new [] {"What is the capital of Spain", "Madrid"},
    new [] {"What is the captial of Russia", "Moscow"},
    new [] {"What is the capital of Ukraine", "Kiev"},
};

// use: questions[0][0] (question), questions[0][1] (answer), questions[1][0] (question)...

or (better) create a class with two members, Question and Answer.

class QuestionAndAnswer
{
    public string Question { get; protected set; }
    public string Answer { get; protected set; }

    public QuestionAndAnswer(string question, string answer)
    {
        this.Question = question;
        this.Answer = answer;
    }
}

QuestionAndAnswer[] questions = new QuestionAndAnswer[] { 
    new QuestionAndAnswer("What is the capital of France", "Paris"),
    new QuestionAndAnswer("What is the capital of Spain", "Madrid"),
    // ...
};

// use: questions[0].Question, questions[0].Answer...

You could then use the Knuth algorithm :-)

Quoting from there:

To shuffle an array a of n elements (indexes 0..n-1):
  for i from n − 1 downto 1 do
       j ← random integer with 0 ≤ j ≤ i
       exchange a[j] and a[i]

In C# the algorithm will be something like

Random rnd = new Random();

for (int i = questions.Length - 1; i >= 1; i--)
{
    // Random.Next generates numbers between min and max - 1 value, so we have to balance this
    int j = rnd.Next(0, i + 1);

    if (i != j)
    {
        var temp = questions[i];
        questions[i] = questions[j];
        questions[j] = temp;
    }
}
Sign up to request clarification or add additional context in comments.

9 Comments

+1 for the class comment. This is not suited at all for a multidimensional array. It should be a simple list of classes.
This also sounds like homework, so he's going to be using tools he has been instructed to use (or taught).
thanks for that the class idea sounds good, but i'm not sure how to link the 2 members together, like i can do with arrays
@AnthonyPegram sort-of-not-quite-homework :) i'm going way above what we're doing in class at the moment - we haven't technically got to arrays yet :)
@LordAro What is your problem? You access the array of questions using [], so questions[0] will give you the first questionAndAnswer, questions[1] the second... and then you access the members like questions[0].Question and questions[0].Answer
|
1

I suggest NOT using a 'multidimensional' array if it is ... not an multidemensional array.

My suggestion: (see it live here http://ideone.com/NsjfM)

using System;
using System.Linq;
using System.Collections.Generic;

public class Program
{
    struct QA { public string Q, A; }

    static Random _r = new Random();        
    static int Quiz()
    {
        var QAndA = new QA[] {
            new QA { Q = "What is the capital of France"  , A = "Paris"}, 
            new QA { Q = "What is the capital of Spain"   , A = "Madrid"}, 
            //  ...
            new QA { Q = "What is the captial of Russia"  , A = "Moscow"}, 
            new QA { Q = "What is the capital of Ukraine" , A = "Kiev"}, 
        };

        foreach (var qa in QAndA.OrderBy(i => _r.Next()))
        {
            Question(qa.Q, qa.A);
        }

        return 0;
    }

    public static void Main(string[] args)
    {
        int n = Quiz();
    }

    private static void Question(string q, string a)
    {
        Console.WriteLine("Q. {0}", q);
        Console.WriteLine("A. {0}", a);
    }

}

Comments

1
maybe better (without shouffling, without repeatable questions):       



class QuizQuestion
{
public string Question {get; set;}
public string Answer {get; set;}
}

static Random _r = new Random();        
        static int Quiz()
        {
            QuizQuestion[] QAndA = new QuizQuestion[] {
                new QuizQuestion() {Question = "What is the capital of France", Answer = "Paris"},
                new QuizQuestion() {Question = "What is the capital of Spain", Answer ="Madrid"},
                        ...
                new QuizQuestion() {Question = "What is the captial of Russia", Answer ="Moscow"},
                new QuizQuestion() {Question = "What is the capital of Ukraine", Answer ="Kiev"},
            };

            var questions = QAndQ.ToList();
            for (int i = 0; i < NUM_QUESTIONS; i++)
            {
                int num = _r.Next(questions.Length / 2);
                Question(questions[num].Question, questions[num].Answer);
                questions.Remove(questions[num]);
            }
        }

3 Comments

List<>.Remove() is O(n), it might be quicker to store 'picked' questions in a set and then only return when the set is of a certain size.
From performannce point of view the best will be generate additionaly list with bool values, which will contain information about usage question in this session. When question is used, then usage[questionIndex] = true. In all steps you will rand number from range 0 to questionCount - i, and check usage of question iterate by usageQuestionList, skipped possition, where questionUsageList[j] is equal true;
Both are O(1), so it depends on the overhead.
0

actually you're reordeing one dimential array, because you shouldn't shufftle answers ;) simplest algorithm may be:

   foreach array index
      switch with random index in array

Comments

0

A method that doesn't require you to reshuffle the array and which will be quicker if you only need to pick a few questions is to store your selected questions in a set.

Keep generating random numbers and adding the question at that index to the set, once the set is of the correct size return it.

Your loop will look something like:

var questions = new HashSet<Question>();
while (questions.Count < numberOfQuestionsRequired)
{
  questions.Add(questionArray[_r.Next()])
}

HashSet<>.Count and HashSet<>.Add() are both O(1) so the limiting factor will be how many random numbers collide.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.