1

I am having difficulties with a project which is creating a lottery machine that prints 6 numbers between 1 and 42 at random where no 2 numbers are the same. The user must also insert 6 numbers. If any number is the same as the one randomly selected by the computer, the computer must print it. If not, the computer prints you are such a loser. Now, the problem is I'm not sure about how to make sure that no 2 randomly selected numbers are the same. The program should also ask for a different number if a number less than 1, greater than 42, or equal to a previous number inserted, and scan it. (user cannot enter 2 identical numbers) PS: I am only a beginner who knows the for loop while loop and if statement so I would love it if the answers were very simple and basic. Please check my code and tell me if there is anything that doesn't work or is illogical. Thank you in advance

 import java.util.Scanner;
 import java.util.Random;

 public class LotoMachine {

public static void main(String[] args) {

    System.out.println("Please enter 6 numbers between 1 and 42.");
    Scanner scan = new Scanner(System.in);

    int[] marks = new int[6];
    Random ran = new Random();
    int[] x = new int[6];
    boolean winner = false;

    for (int i = 0; i < 6; i++) {
        marks[i] = scan.nextInt();
    }
    for (int j = 0; j < 6; j++) {
        x[j] = ran.nextInt(42) + 1;
        for (int y = 0; y < j; y++) {
            if (x[j] == x[y]) {
                x[j] = ran.nextInt(42) + 1;
                j=0;
            }
        }
    }

    for (int m = 0; m < 6; m++) {
        System.out.println(x[m]);
    }
    for (int i = 0; i < 6; i++) {
        for (int j = 0; j < 6; j++) {
            if (marks[i] == x[j]) {
                winner = true;
                System.out.println("Number(s) that matched: " + marks[i]);
            }
        }
    }
    if (winner != true) {
        System.out.println("You are such a loser");
       }
   }
}
  • This is what I tried but I'm not sure if it works for (int j = 0; j < 6; j++) { x[j] = ran.nextInt(42) + 1; for (int y = 0; y < j; y++) { if (x[j] == x[y]) { x[j] = ran.nextInt(42) + 1; j--; } } } –  May 04 '17 at 13:55
  • 2
    use a while statment and keep producing random number until there is no match in the random number array – brad May 04 '17 at 13:57
  • 1
    Possible duplicate of [Creating random numbers with no duplicates](http://stackoverflow.com/questions/4040001/creating-random-numbers-with-no-duplicates) – Robin Topper May 04 '17 at 14:09
  • This is too complicated for me I'd rather it be more simple because most of the code was very new and strange to me in the link ( still a beginner) –  May 04 '17 at 14:22
  • I added extra info –  May 09 '17 at 12:42

6 Answers6

3

The Fisher Yates Shuffle is rapidly becoming my goto answer for everything on Stackoverflow.

You should do the following:

  • create an array with the 42 numbers in it in ascending order. The array is indexed between 0 and 41.
  • Generate a random number r0 where 0 <= r0 < 42
  • swap the number at index 0 with the number at index r0.
  • Generate a random number r1 where 1 <= r1 < 42
  • swap the number at index 1 with the number at index r1.
  • Generate a random number r2 where 2 <= r2 < 42
  • swap the number at index 2 with the number at index r2.

and so on until you have swapped the number at index 5. Obviously it is trivial to put the above steps in a loop. Also note that it is not a bug to swap a number with itself.

The first six numbers in your array (indexes 0 through 5) are the selected lottery numbers.

The algorithm can be generalised to select any n unique items from m objects. For instance, having an array of 52 items and going through all 52 is a handy way to model shuffling a pack of cards.

Here's some code to implement the algorithm (I haven't compiled or tested it so there might be mistakes

Random random = new java.util.Random();

int numbers[] = new int[42];

// create the initial array
for (int i = 0 ; i < 42 ; ++i)
{
    numbers[i] = i + 1;
}

// shuffle
for (int i = 0 ; i < 6 ; ++i)  
{
    int ri = random.nextInt(42 - i) + i; // generates a random index between i and 42
    int tmp = numbers[ri];
    numbers[ri] = numbers[i];
   numbers[i] = tmp;
}

// your six lottery numbers are in numbers[0] to numbers[5]
JeremyP
  • 84,577
  • 15
  • 123
  • 161
  • 1
    _Obviously it is trivial to put the above steps in a loop_ .... I don't think someone just starting with Java would agree – Robin Topper May 04 '17 at 14:14
  • @RobinTopper Judging from the code fragment in the question, I'd say `user7963567` is already familiar with the concept of a loop. Also, for six numbers, you can leave it unrolled. – JeremyP May 04 '17 at 14:23
0

There I fixed it. You had to reset your j to 0 when you find matching numbers AND you can't start from 0 in both for's because then you'll always be comparing the same numbers at index 0.

P.S. Learn while loop

import java.util.Scanner;
import java.util.Random;

 public class LotoMachine {

public static void main(String[] args) {

    System.out.println("Please enter 6 numbers between 1 and 42.");
    Scanner scan = new Scanner(System.in);

    int[] marks = new int[6];
    Random ran = new Random();
    int[] x = new int[6];
    boolean winner = false;

    for (int i = 0; i < 6; i++) {
        marks[i] = scan.nextInt();
    }
    for (int j = 0; j < 6; j++) {
        x[j] = ran.nextInt(42) + 1;
        for (int y = 0; y < j; y++) {
            if (x[j] == x[y]) {
                x[j] = ran.nextInt(42) + 1;
                j=0;
            }
        }
    }

    for (int m = 0; m < 6; m++) {
        System.out.println(x[m]);
    }
    for (int i = 0; i < 6; i++) {
        for (int j = 0; j < 6; j++) {
            if (marks[i] == x[j]) {
                winner = true;
                System.out.println("Number(s) that matched: " + marks[i]);
            }
        }
    }
    if (winner != true) {
        System.out.println("You are such a loser");
       }
   }
}
  • thanks a lot btw can you explain what you did a bit more please –  May 04 '17 at 14:27
  • btw thanks for your answer do you think this is a simple way of reaching my goal instead of the Fisher Yates Shuffle and if I were to use the shuffle how would I write it in code @JeremyP –  May 04 '17 at 14:33
  • when you find a duplicate number you need to reset the for loop from the start because a new number might be contained in the previously added ones.. – Black Flicka May 04 '17 at 14:34
  • so I don't decrement j ? I reset it from the beginning instead? if I decrement j why wouldn't that work? @JeremyP –  May 04 '17 at 14:38
  • because new generated number can be the same number you have already checked before – Black Flicka May 04 '17 at 14:47
  • i mean this code is not completely correct, whenever you find a duplicate number you create a new set of 6 which is a problem.. so it will be stuck in for loop until it creates 6 unique numbers. but considering how it's only 6 numbers out of 42 it won't pose a problem you could actually notice in performance. – Black Flicka May 04 '17 at 14:48
  • the first number that is printed for some reason is always 0 –  May 04 '17 at 15:34
0

The easiest and most efficient thing is to put the possible numbers in a List, then remove a random element from the list until you have as many as you need:

 // create a list containing 1 .. 42
 List<Integer> available = new ArrayList<>();
 for(int i=1; i<=42; i++) {
     available.add(i);
 }

 // pull 6 numbers from `available` at random
 List<Integer> picks = new ArrayList<>();
 for(int i=0; i<6; i++) {
     picks.add(available.remove(random.nextInt(available.size());
 }

You can do something similar with arrays, but it's more complicated because you'd have to write your own array equivalent of List.remove(). Perhaps you've not encountered List yet in your studies -- but if you want a simple solution, you need to use the appropriate tools, and lists are easier to use than arrays.


Alternatively, you can just use arrays if you want to make life slightly harder for yourself.

// create an array containing 1..42
int[] available = new int[42];
for(int i=0; i<42;i++) {
    available[i] = i+1;
}

// pull out 6 numbers
int[] picks = new int[6];
int availableSize = 6;
for(i=0; i<6;i++) {
    int r = random.nextInt(availableSize);
    // grab the pick
    picks[i] = available[r];
    // move an unused number over the picked one
    availableSize--;
    available[r] = available[availableSize];
}

Each time we take a number, we reduce availableSize by one, and overwrite the number we've taken from available with an unused one from the top of what's left of the numbers. So for example, at the start (let's go with 6 candidates instead of 42):

available == [1,2,3,4,5,6]
availableSize == 6
r = 3 // for example
picks[0] becomes available[3] == 3
availableSize becomes 5
available becomes [1,2,6,4,5,6]

... but the second 6 in available is irrelevant, because next time around we'll only pick from the first 5 elements.

slim
  • 40,215
  • 13
  • 94
  • 127
  • It's a bugbear of mine that teachers seem to make students use arrays before Collections. It's important to understand arrays, but I think it's better to get used to programming constructs on higher level APIs first. – slim May 04 '17 at 14:43
  • is it possible to do it without a List cause you're right I've never encountered it before ever –  May 04 '17 at 14:57
  • It's possible -- see above -- but IMO you should learn List before arrays. – slim May 04 '17 at 15:18
0

There are two different ways how you can solve this. In both you need to make trade of either for using more memory or to perform more computations.

The first one uses more memory but is computationally efficient

int[] numbers = new int[42];
int len = 42;
for (int i = 0; i < 42; i++) {
    numbers[i] = i + 1;
}

for (int i = 0; i < 6; i++) {
    int pos = rng.nextInt(len);
    x[i] = numbers[pos];

    for (int j = pos + 1; j < len; j++) {
        numbers[j - 1] = numbers[j];
    }
    pos--;
}

The second approach is computationally more expensive but memory efficient

int pos = 0;
while (pos < 6) {
    int number = rng.nextInt(42) + 1;
    boolean duplicate = false;
    for (int i = 0; i < pos; i++) {
        if (x[i] == number) {
            duplicate = true;
        }
    }

    if (!duplicate) {
        x[pos++] = number;
    }
}

The first one creates a list of unique number and draw from this list. Afterwards the drawn number is removed form the list. the second approach draw random numbers and checks if the drawn number already exists, if so it is discarded.

Westranger
  • 1,308
  • 19
  • 29
0

To be a bit more easy for the eye and using modern Java8 streams you could use the Collections API to shuflle to be a bit more clear:

private static Set<Integer> generateXuniqueNumbersFromRange(int unique, int start, int end) {
    List<Integer> availableLotteryNumbers = IntStream.rangeClosed(start, end).boxed().collect(Collectors.toList());
    Collections.shuffle(availableLotteryNumbers);
    Set<Integer> lotteryNumbers = availableLotteryNumbers.stream().limit(unique)
            .collect(Collectors.toCollection(TreeSet::new));
    return lotteryNumbers;
}

//// Rest of the code (also rewritten)

public static void main(String[] args) {
    Set<Integer> lotteryNumbers = generateXuniqueNumbersFromRange(6, 1, 42);

    Set<Integer> userSelection = new TreeSet<>();

    try (Scanner scan = new Scanner(System.in)) {
        while (userSelection.size() < 6) {
            int nextInt = scan.nextInt();
            if (nextInt <= 42 && nextInt >= 1) {
                userSelection.add(nextInt);
            } else {
                System.out.println("Select between 1 - 42");
            }
        }
    }

    System.out.println("You had these numbers " + userSelection);
    System.out.println("The lottery selected " + lotteryNumbers);
    userSelection.retainAll(lotteryNumbers);

    if (userSelection.isEmpty()) {
        System.out.println("You are such a loser");
    } else {
        System.out.println("You had " + userSelection.size() + " correct ones " + userSelection);
    }

}
Viktor Mellgren
  • 4,318
  • 3
  • 42
  • 75
-1

answer from Creating random numbers with no duplicates

The code (for you) is:

Random rng = new Random(); // Ideally just create one instance globally
// Note: use LinkedHashSet to maintain insertion order
Set<Integer> generated = new LinkedHashSet<Integer>();
while (generated.size() < 6) //repeat as long as generated.size() < 6 -> means you dont have 6 unique integers
{
    Integer next = rng.nextInt(42) + 1;
    // As we're adding to a set, this will automatically do a containment        check
generated.add(next);
}
int[] lottery_numbers =  generated.toArray();
//Do want you want to do with the 6 lottery numbers
Community
  • 1
  • 1
qry
  • 457
  • 3
  • 11
  • I'm still a beginner so I haven't learned the do while loop yet what does it and is there an alternative –  May 04 '17 at 14:09
  • **while** ( __the condition here is true__ ){ __do this, whats inside here__ }. Really simple – qry May 04 '17 at 14:31
  • I actually recall learning such a thing but probably forgot cause I haven't used it any way thanks @LikeBlender –  May 04 '17 at 14:36
  • So i rewrote the code. Now you can simply use this and not x and your x generation algorith. if you dont understand it, simply ask :) – qry May 04 '17 at 14:42
  • To be honest, i dont know who downvote this solution, its the most beautiful and most efficient of all of those here. – qry May 04 '17 at 14:43
  • I haven't downvoted, but it could conceivably loop forever (there's nothing to stop a *really* random source from returning the same number over and over again). – slim May 04 '17 at 14:51
  • @slim yes thats true :D but it wont happen without manipulating the random function ^^ and its a lot faster than the other answers – qry May 04 '17 at 14:56