0

Ok, so the scenario is, I want to generate a list of 4 distinct random numbers which will represent 4 random choices for a quiz application. One of the 4 random choices will be the correct answer, so we will already know the index of the correct choice. This correct index or number must be included in the random number list.

For example: Consider that we have an array of length 100, containing string values representing 100 choices for a question, and the index of correct choice is 45. Now we want 4 random choices for this question including the index 45, so that the index list will be something like {2, 91, 45, 17}. Also the list shouldn't contain duplicate numbers.

Any idea how to achieve this in Java ?

Kuldeep Kumar
  • 865
  • 3
  • 9
  • 19
  • You could get three random numbers as described in http://stackoverflow.com/questions/363681/generating-random-integers-in-a-specific-range and add the "right index" to your array of possible answers. To avoid to have the right answer always at the same position you could shuffle the array. – Anton Jan 22 '17 at 10:09
  • Generate 3 random number with min 0 and max array size-1 and check if the given value is already picked. 3 because you know your answer. we did something similar this way. – user2660616 Jan 22 '17 at 10:10
  • Possible duplicate of [How to generate 6 different random numbers in java](http://stackoverflow.com/questions/22584244/how-to-generate-6-different-random-numbers-in-java) – McGrady Jan 22 '17 at 10:13

3 Answers3

4

For Java 6 and newer:

final int maxNumber = 100;
final int numbersToGenerate = 4;
final int correctAnswer = 45;

Set<Integer> possibleAnswers = new HashSet<>();
Random random = new Random();

// add correct answer
possibleAnswers.add(correctAnswer);

// add as much random answers as needed, the usage of a set prevents duplicates
while(possibleAnswers.size() < numbersToGenerate) {
    possibleAnswers.add(random.nextInt(maxNumber));
}

// convert set to list and shuffle it
List<Integer> answers = new ArrayList<Integer>(possibleAnswers);
Collections.shuffle(answers, new Random(System.nanoTime()));

For Java versions below 6 you have to write your own shuffle method, because Collections.shuffle was introduced in Java 6, as far as I know.

I first suggested to use the random api of Java 8, but found an bug in my idea. If the array of generated random numbers contains the correct answer it will not work. For your understanding:

NOT WORKING!!!

final int minNumber = 1;
final int maxNumber = 100;
final int numbersToGenerate = 3;

final int[] ints = new Random().ints(minNumber, maxNumber)
.distinct().limit(numbersToGenerate).toArray();

List<Integer> possibleAnswers = asList(ints);
possibleAnswers.add(correctAnswerIndex);
Collections.shuffle(possibleAnswers, new Random(System.nanoTime()));

NOT WORKING !!!

Anton
  • 429
  • 4
  • 14
  • According to your answer it works in Java 8. You need to describe what will you do in older Java, there are situations when there is a legitimate reason to use older Java. Also, you need to generalize, the number of possible answers is not necessarily constant. As you can see, your answer has several shortcomings, but that is not a reason to down-vote it, as it is helpful in the limited use-cases it can be used in. – Lajos Arpad Jan 22 '17 at 11:02
  • Anton your answer should be as general as possible. You should never assume that the current reader AND the future readers are ALL using Java 8. Your solution is good for a small number of use-cases. People can use it if its limited number o use-cases applies to them. If not, then they need something else. You need to edit your answer and make your code more general, preferably with variable number of possible responses and a solution for viewers using older Java. Unless you do that, your answer is incomplete. – Lajos Arpad Jan 22 '17 at 11:06
  • Your answer assumes that the number of elements is 100. It shuffles all the items, so if the number of attributes get bigger and the operation is repeated in a cycle many times, then your code will be slow and in a multithreaded environment it will not scale well. The answer is not explained at all, it just copies a code which is not understandable to the op (that's why he asked) and leaves a mystery to the users about what happens. So this answer is bleeding from 1000 wounds. – Lajos Arpad Jan 22 '17 at 11:19
  • Oh thats nice. I never knew Random was bound to the streaming api as well. Learned something new today – Michael Ritter Jan 22 '17 at 11:59
  • Hi Anton, I could be wrong, but I guess this code doesn't generate a random number list that contain distinct numbers. It may have numbers that could be duplicates. – Kuldeep Kumar Jan 22 '17 at 13:44
  • @KuldeepKumar before i have posted this solution I wrote some unit tests which were successful. I avoid duplicates by using a Set of Integers. A Set only holds distinct values. But lets have a look at an example: You have a set with {3, 42, 64} and if you try to add 42 again, the set will still looks like before. Because the Set already contained a 42. Have a look at https://docs.oracle.com/javase/7/docs/api/java/util/Set.html for add(). – Anton Jan 22 '17 at 13:48
  • This solution worked great for me Anton :) Didn't knew about sets in Java so I wan't aware that it allows only unique values. Thanks for the solution :) – Kuldeep Kumar Jan 23 '17 at 13:55
1

This class could help you

public class RandomQuiz {

    //The number of possible answers
    private int size;
    //The number of possible indexes
    private int n;
    //The correct index
    private int correct;

    //Constructor
    public RandomQuiz(int size, int n, int correct) {
        this.size = size;
        this.n = n;
        this.correct = correct;
    }

    //Returns size number of shuffled random indexes
    public int[] getRandomIndexes() {
        //The result set
        int[] result = new int[size];
        //We start with the correct index in the first place, so random values will be entered starting from the second place
        int index = 1;
        //First thing's first
        result[0] = correct;
        Random random;
        while (index < size) {
            //We always decrease the number of seeds
            random = new Random(n - index);
            //Getting a random value
            int randomized = random.nextInt();
            //Ensuring the numbers are not duplicate
            for (int i = 0; i < index; i++) if (randomized >= result[i]) randomized++;
            result[index++] = randomized;
        }
        //Randomize where correct will be at the end:
        random = new Random(size);
        int newIndex = random.getNextInt();
        //If the new index of correct is bigger than 0
        //than swap it with the item located on newIndex
        if (newIndex > 0) {
            result[0] = result[newIndex];
            result[newIndex] = correct;
        }
        return result;
    }
}

EDIT:

In a private chat with Anton he told me that some parts are unclear, namely:

  • why did I decrease the number of seeds
  • why did I increase randomized in a cycle

The number of seeds is decreased since we can use any number once maximum. If the seed was 100, then after the first item was chosen, it becomes 99 and so on. To answer the second question: if 45 was chosen and then a number at least of 45 is chosen, then we need to add 1 to that number to cope with the gap left when we have chosen 45. If there were some numbers chosen and we choose a new number, then we need to add to that number the number of gaps below it, that is, the number of already chosen smaller or equal numbers to cope with all the gaps.

Note that nothing was taken personally, I would leave the kind of comments I have left here if somebody else's correct answer was down-voted as well. I am not against my answer being down-voted, but against down-voting correct answers in general.

Lajos Arpad
  • 45,912
  • 26
  • 82
  • 148
  • far to complicated and needs to many lines of code. As proven the amount of bugs is not rising with the complexity of the code but with the lines of code! – Anton Jan 22 '17 at 10:48
  • @Anton "to complicated" is not a reason to down-vote an answer. Down-vote means that the answer is unhelpful. So, please tell me how was my answer unhelpful and suggest improvements. Or revoke your down-vote. – Lajos Arpad Jan 22 '17 at 10:54
  • Well in my opinion we should not give just an answer "that works", furthermore we should give a good answer to help people improve their programming. And in my eyes is a too complicated solution/answer not helpful for the improvement of development skills. And in addition to that i would like to see a bit more explanation that just pasting a class. – Anton Jan 22 '17 at 11:01
  • 2
    @Anton, this answer shows the algorithm, so it IS improving the programming abilities of readers. And it is perfectly illegitimate to down-vote an answer which is solving the problem. Your criticism is welcome, but unless you can show the shortcomings of this answer (a bug, for example) you are being incorrect in down-voting it. Also, my answer is explained in comments (adding to the "many lines"), not just a copy-paste as in your answer. – Lajos Arpad Jan 22 '17 at 11:09
  • @Anton you are saying that the code is complicated for you. Can you tell me which part is difficult to understand so I can elaborate it? I will be a gentleman and will not assume that the reason of your down-vote was to improve the chances of your answer to be accepted. I have no problem with your answer being accepted if its quality is improved. Yet, as matters stand right now, my answer was given earlier, it should work in many environments, it is explained in the comments. Your answer lacks all these qualities, yet you down-vote mine. – Lajos Arpad Jan 22 '17 at 11:15
  • @Anton the class was not pasted into the answer, but written by hand. The comments explain all the relevant ideas. You should really tell me where you found bugs in my answer. I also kindly ask you to get back here after a few years and reread your comments. You will perfectly understand me when some beginners will down-vote your answer because it is above their current knowledge. – Lajos Arpad Jan 22 '17 at 11:29
  • Wow, I did not want to offend you or something. It seems you are taking it personal. BUT, why are you decreasing the number of seeds? -> explain it! Code like 'for (int i = 0; i < index; i++) if (randomized >= result[i]) randomized++;' is not easy to understand for beginners and just to add +1 when you have a duplicate seems not to be the best solution. – Anton Jan 22 '17 at 11:49
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/133738/discussion-between-anton-and-lajos-arpad). – Anton Jan 22 '17 at 11:50
0

I wrote a full program based on your needs. However please take a look at what I am doing. With just a little context, this is what I created:

     // initialize a random object once.
     Random random = new Random();
     // the question
     String question = "With what letter does the name start of the new president of the USA?";
     // here are some basic answers
     String[] answers = new String[] {
      "a",
      "b",
      "c",
      "d",
      "e",
      "f",
      "g",
      "h",
      "i",
      "j",
      "k"
     };
     // you already know the correct index of the array above, in this case it's d
     int index = 3;
     // this array will contain four answers, including correct one!
     String[] four = new String[4];
     // get answer index, we will place correct value in that index
     int answerIndex = random.nextInt(four.length);
     // now lets pick 4 answers!
     for (int i = 0; i < four.length; i++) {
      // we are at the answer index!
      if (i == answerIndex) {
       four[i] = answers[index];
       continue;
      }
      int randomIndex = random.nextInt(answers.length);
      for (int j = 0; j < four.length; j++) {
       // we got duplicate here!
       if (answers[randomIndex].equals(four[j])) {
        randomIndex = random.nextInt(answers.length);
        // redo this current iteration
        j = j - 1;
       }
      }
      four[i] = answers[randomIndex];
     }

Output:

e, c, d, h
g, d, d, h
d, g, e, f
d, f, b, i
g, d, a, b
c, d, g, b
h, d, e, k
e, f, d, c
k, d, e, h
i, d, e, d

It will help if you explain where you are using it for, as well as a short demonstration at what you have already coded.

Giovanni
  • 6,055
  • 2
  • 23
  • 44