I need help with this part of my code. This part deals with generating 2 random numbers and using that random number to display a card in each of 2 label boxes simultaneously. The problem here is the random numbers are not getting generated properly and thus cards are not displayed properly (there is repetition, sometimes no display, etc)
Basics of my code:
Let us assume h, which is a variable from another part of the code, is any number between 1 and 53 (each number pertaining to a card). If the random number generated (non-repetition) matches the variable h the timer stops.
So its basically like having a deck of cards and dealing out the cards evenly to 2 people, but here the dealing stops once a number pertaining to a card (number) randomly taken is matched.
(l3,l4 are label names)
Global variables:
Random rng = new Random();
List<Integer> generated = new ArrayList<Integer>();
List<Integer> generated2 = new ArrayList<Integer>();
int l3count;
int l4count;
int Ulim = 53;
int Llim = 1;
int next;
int check;
int h;
int next2;
int Ulim2 = 53;
int Llim2 = 1;
final int p = h;
int delay2 = 1000;
final Timer timer2 = new Timer();
timer2.schedule(new TimerTask(){
public void run(){
for (int i = 1; i < 53; i++)
{
while(true)
{
next = rng.nextInt(Ulim) + Llim;
if (!(generated.contains(next)||generated.contains(next2)))
{
generated.add(next);
break;
}
next2 = rng.nextInt(Ulim2) + Llim2;
if (!(generated.contains(next)||generated.contains(next2)))
{
generated.add(next2);
break;
}
}
String a = Integer.toString(next);
String c = "C:\\Users\\mycompname\\Desktop\\deck\\"+a+".png";
String d = Integer.toString(next2);
String e = "C:\\Users\\mycompname\\Desktop\\deck\\"+d+".png";
for(int j = 1;j<=53;j++)
{
if(j%2==0)
{l3.setIcon(new ImageIcon(c));
}
else
{l4.setIcon(new ImageIcon(e));
}
}
if(next==p||next2==p)
check=10;
break;
}
if(check==10)
timer2.cancel();
timer2.purge();
}
},delay2, 1000);
Any help would be appreciated.
Thanks for your time.
You'd be better off dealing the cards out randomly.
Using the O(n) Knuth Shuffle you can pass along one card at a time, from one array to another.
Each new array you pass the card to will be a different persons' hand.
Keep adding 1 card at a time to each array from the deck array until each player has the number of cards your rules require.
Also, don't rely on random numbers being equal. See stupid-sort. Look for some other pattern instead. Like a random number of cards % cards_left you should stop giving out cards.
Update with code-sample, as per request:
public static void shuffle (int[] array) {
for(int n = array.length; (n > 1);) {
int k = gen.nextInt(n--);
int temp = array[n];
array[n] = array[k];
array[k] = temp;
}
}
The obvious problem is that Random.nextInt generates a number 0 (inclusive) and n (exclusive). So when you pass in a limit of 53, you're generating values between 0 and 52 — that's 53 possible values, and there aren't that many cards. So change the upper limit to 52.
Also, as other answers note, the reason you're getting collisions (dealing the same card to multiple hands), etc. is that you don't want to randomly select the cards. You actually want to randomly shuffle the deck.
Try using a shuffle algorithm instead.
Related
I made a code that could generate random numbers, but my issue is that sometimes it would repeat two or three numbers from time to time.
int winnum[] = new int[6];
System.out.print("Lotto winning numbers: ");
for(int i = 0; i < winnum.length; i++){
winnum[i] = 0 + (int)(Math.random() * 42);
System.out.print(winnum[i] + " ");
}
here is my code for generating random numbers
A fairly easy way to do this is to re-imagine this a little bit. Instead of 'generate a random number', think about a deck of cards. You don't 'generate random cards' - you generate (well, print) 52 cards in a specific order and then you randomize their order. Now you can draw cards off the top of the deck and they'll be random, but they cannot repeat.
Do the same thing: Make a list, fill it with all 42 numbers, then shuffle the list, then pull the top 6 numbers off of the list. Voila: Random - and without repetition.
var numbers = new ArrayList<Integer>();
for (int i = 0; i < 42; i++) numbers.add(i);
Collections.shuffle(numbers);
for (int i = 0; i < 6; i++) {
System.out.print(list.get(i) + " ");
}
Here is a real quick way using streams.
Random r = new Random();
r.ints(n,m) - generate a stream of random values between n and m-1 inclusive
distinct - ensure they are unique
limit(n) - limit to n
toArray - return as an array.
int [] result = r.ints(0,42).distinct().limit(6).toArray();
System.out.println(Arrays.toString(result));
prints something like
[37, 19, 28, 31, 15, 12]
You need to check whether the array contains the newly generated random number. If not, you add it, otherwise, keep generating random numbers until one that's not in the array is generated.
To check whether the number is found in the array, you can write the following function that takes the random integer, your array and returns a boolean:
boolean exists = false;
for (int n : winnum) {
if (n == randomInt) {
exists = true;
break;
}
} return exists;
my goal is to randomly shuffle an array, (from 0 to 9) but every number has to appear in the array only once. I have got two (working) ideas, but I would like to find out how many times must this random2 method iterate to achieve the same level of randomness in array as in the first method (random1).
import java.util.Random;
class RandomStuff {
static Random r;
final static int iteraction = 10;
public static void main (String[] args) {
r = new Random();
int[] array = new int[10];
random1(array);
random2(array, iteraction);
}
static void random1(int[] array) {
for(int i = 0; i < array.length; i++) pole[i] = -1;
for(int i = 0; i < array.length; i++) {
while(true) {
int y = r.nextInt(10);
if(!find(array, y)) {
array[i] = y;
break;
}
}
}
}
static void random2(int[] array, int iteraction) {
for(int i = 0; i <= iteraction; i++) {
int y1 = r.nextInt(array.length);
int y2 = r.nextInt(array.length);
int p = array[y1];
array[y1] = array[y2];
array[y2] = p;
}
}
static boolean find(int[] array , int value) {
for(int i = 0; i < array.length; i++) {
if(pole[i] == value) return true;
}
return false;
}
}
The first method (random1) works assigning of random numbers and testing, if they are/aren't in the array already. Which seems to be pretty random to me.
The second method (random2) works on swaping two random random values in the array. So the question is, how many times do I have to swap two numbers in the array to achieve the same level of randomness. (or what value shoud the variable iteraction have).
Thanks for any reply.
How about assigning a random number to each element of the array, arrange the random numbers in order and in that order read the element of the array assigned to that random number
0.64342 0
0.95229 1
0.23047 2
0.82793 3
0.19014 4
0.18528 5
0.15684 6
0.99546 7
0.54524 8
0.90612 9
Order
0.15684 6
0.18528 5
0.19014 4
0.23047 2
0.54524 8
0.64342 0
0.82793 3
0.90612 9
0.95229 1
0.99546 7
numbers 0 to 9 now in random order
To answer your original question, "how many times must this random2 method iterate to achieve the same level of randomness in array as in the first method?"
The answer is: it will never achieve the same level of randomness.
For any position that has been swapped, there is an equal chance of it arriving in any position, which means a 10% chance it ends up back where it started.
In each iteration, 2 numbers are swapped (or zero if the number is swapped to its own position). That means there's an 80% chance for any given position to never have been swapped, after 1 iteration. After N iterations, there is still a 0.8^N chance that it was never swapped. If it was swapped, there is a 10% chance it went back where it started. So the probability that any given digit is in its starting position is 10% + 0.8^N. This is always > 10%, so you will never get a perfectly even distribution.
For example, for your choice of 10 iterations, there remains a 10.7% chance for each digit that it never moved, or a total of 19.7% chance it'll be in its starting position. So ten iterations is not even close to enough.
I want to generate 6 different random numbers by using Math.random and store them into an array.
How can I make sure that they are different? I know I need to use for-loop to check the array but how...
This is the range. I only need numbers between 1 and 49.
( 1 + (int) (Math.random() * 49) )
In Java 8:
final int[] ints = new Random().ints(1, 50).distinct().limit(6).toArray();
In Java 7:
public static void main(final String[] args) throws Exception {
final Random random = new Random();
final Set<Integer> intSet = new HashSet<>();
while (intSet.size() < 6) {
intSet.add(random.nextInt(49) + 1);
}
final int[] ints = new int[intSet.size()];
final Iterator<Integer> iter = intSet.iterator();
for (int i = 0; iter.hasNext(); ++i) {
ints[i] = iter.next();
}
System.out.println(Arrays.toString(ints));
}
Just a little messier. Not helped by the fact that it's pretty tedious to unbox the Set<Integer> into an int[].
It should be noted that this solution should be fine of the number of required values is significantly smaller than the range. As 1..49 is quite a lot larger than 6 you're fine. Otherwise performance rapidly degrades.
Create a list containing the numbers 1 to 49.
Create a random number x between 0 and the size of the list, take the number being at index x in the list, and remove it from the list.
Repeat the previous step 5 times. And you're done. Note that java.util.Random has a nextInt(int max) method that you should use instead of Math.random().
Note regarding performance: this solution has an advantage compared to the "try until you get 6 different numbers" various solutions: it runs in a O(n) time. It doesn't matter much for 6 unique numbers out of 50, but if you want to get 48 or 49 unique random numbers out of 50, you'll start seeing a difference, because you might have to generate many random numbers before getting one that isn't already in the set.
EDIT:
to reduce the cost induced by the removal of the elements in the list, you could instead simply replace the element at index x with the last element of the list (and at the second iteration, with the element at size - 2, etc.)
You can use a Set.
Set<Integer> s = new HashSet<>();
while(s.size() != 6){
s.add(1 + (int) (Math.random() * 49));
}
Integer[] arr = s.toArray(new Integer[s.size()]);
This is enough to do this in your case because the number of distinct random numbers is relatively small compared to the size of the range you generate them.
Otherwise I would go with #JBNizet approach.
Generate any 6 numbers (not necessarily different). Order them.
a1 <= a2 <= a3 <= a4 <= a5 <= a6
Now take these 6 numbers
a1 < a2 + 1 < a3 + 2 < a4 + 3 < a5 + 4 < a6 + 5
These 6 are different and random.
The idea of this construct comes from some combinatorial proofs.
Its advantage is that it's simple, fast, and deterministic.
I think the time complexity is O(count*log(count)).
I wonder if it can be improved.
import java.util.TreeMap;
public class Test005 {
public static void main(String[] args) {
int count = 6;
int min = 1;
int max = 49;
// random number mapped to the count of its occurrences
TreeMap<Integer, Integer> mp = new TreeMap<Integer, Integer>();
for (int i=0; i<count; i++){
int d = ( min + (int) (Math.random() * (max-count+1)) );
if (!mp.containsKey(d)){
mp.put(d, 0);
}
mp.put(d, mp.get(d) + 1);
}
// now ensure the output numbers are different
int j = 0;
for (int num : mp.keySet()){
int cnt = mp.get(num);
for (int i=0; i<cnt; i++){
System.out.println(num + j);
j++;
}
}
}
}
I've just came up with a small idea for Java 8-.
Set<Integer> set = new LinkedHashSet<>();
while(set.size() != 6)
set.add(rnd.nextInt(49) + 1);
Instead of checking that the array has no duplicates, you can use a bit more smartness while generating the numbers, such that uniqueness is enforced at the outset.
Create a boolean[] as long as your range (49 entries);
generate a random number from the full range;
put that number into your output array;
"cross out" the corresponding index in the boolean[];
now generate another random number, but curtail the range by one (now 48);
instead of directly using that number as output, scan your boolean[], counting all the non-crossed entries. Stop when you reach the count equal to the random number generated in step 5. The number corresponding to that entry is your output number;
go to step 4.
in your case n=6
public static int[] chooseAny(int n){
int[] lottery = new int[n];
int[] chooseFrom = new int[49];
for(int i=1 ; i <= 49 ; i++)
chooseFrom[i-1] = i;
Random rand = new Random();
int N = 49;
int index;
for(int i=0 ; i < n ; i++){
//pick random index
index = rand.nextInt(N);
lottery[i] = chooseFrom[index];
chooseFrom[index] = chooseFrom[N-1];
N--;
}
return lottery;
}
Just keep generating numbers and adding them to the array as long as they are unique; psuedocode:
num = genNextRand()
For (array length)
If (num not in array)
addToArray()
Repeat while length not equal 6
Create a variable last; initialize it to 0.
Next, in a loop x from 0 to 5, create a random number between last+1 and 49-6+x. Store this number in a list, and set last to the number generated this way.
You will end up with an ordered list of 6 random numbers in the range of 1..49 with no repeats.
That code generate numbers from 6 to 0 and save in ArrayList.
If generated number was duplicated the program generate numbers again.
If generated number is different that number is added.
Code:
private ArrayList<Integer> arraylist = new ArrayList<Integer>();
private Random rand = new Random();
public void insertNumber() {
while (true) {
int i = generateNumber();
if(!isGenerateNumberExists(i)){
addNumber(i);
break;
}
}
}
//Generate numbers
private int generateNumber() {
return rand.nextInt(6);
}
//Confirm if that number exists
private boolean isGenerateNumberExists(int y) {
for (int num : arraylist) {
if (num == y) {
return true;
}
}
return false;
}
//Add number to arrayList
private void addNumber(int x) {
arraylist.add(x);
}
The following code shows 4 int variables:
int xy1 = 724329;
int xy2 = 714385;
int xy3 = 715440;
int xy4 = 696492;
I'm pretending to code an app that, by opening it, shows one of those numbers (NOT numbers between them) on java console, randomly. I know that Math.Random class can be used to solve these kind of issues, but I don't know what is the proper way to do so.
So, thanks.
Well it sounds like you just want a collection of possible values, and an index between 0 and 3 inclusive:
int[] values = { 724329, 714385, 715440, 696492 };
Random random = new Random(); // Ideally initialize once for the entire app
int index = random.nextInt(4);
int value = values[index];
Place them into an array and use Random to select a number between 0-3 and use it as a key to select the value from the array.
Try this one.
This line
r.nextInt(nums.length)
selects an integer from 0 to nums.length-1.
Then I print out the randomly selected number from the nums array.
I repeat this 20 times just for demonstration purposes.
import java.util.Random;
public class Test015 {
public static void main(String[] args) {
int[] nums = {724329, 714385, 715440, 696492};
Random r = new Random();
for (int i=0; i<20; i++){
int index = r.nextInt(nums.length);
System.out.println("Number randomly chosen: " + nums[index]);
}
}
}
I want to generate random numbers within the range 1 to 4, 4 including.
Here is my code:
int num = r.nextInt(4) + 1; //r is instance of Random.
However, I am running the above code in a loop and don't want repeating random number.
What happens now is often I am getting:
1,1,1,2,3,1,4,2,2,1,4,2,4,4,2,1,4,3,3,1,4,2,4,1 as my output.
Here, though the numbers are random within the range(1-4), but often repeated like the number "1"in the first 3 iterations.
What I am looking for is a way to get non repeating random number within the loop.
One simple way I know is of keeping the last random number before current iteration and compare, but I am sure there must be better solution to this.
Thanks in advance.
Use random.nextInt(range-1) and then map that number to the output number with a function that excludes the previous number:
public class Test {
private final Random random = new Random();
private final int range;
private int previous;
Test(int range) { this.range = range; }
int nextRnd() {
if (previous == 0) return previous = random.nextInt(range) + 1;
final int rnd = random.nextInt(range-1) + 1;
return previous = (rnd < previous? rnd : rnd + 1);
}
public static void main(String[] args) {
final Test t = new Test(4);
for (int i = 0; i < 100; i++) System.out.println(t.nextRnd());
}
}
There is no "better" answer.
You are getting a random number. Check this line:
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
this could be perfectly random.
So I propose you describe a better requirement.
Do you want always a next number that is different from the previous? Do you want a maximum of duplicates in a special rang? lets say within 6 consecutive numbers, every number is allowed to occur twice?
If you bring such a requirement we might be able to help you. otherwise we can just say: what you seeing is really random :)
As you have more numbers than you have to choice from you have to repeat some numbers. All you can do is minimise the number of immediate repeats.
One way to do this is to use Collections.shuffle which allow you to have numbers in a random order, without repeats and do this each time. You could prevent the last N value being repeated.
To stop consecutive repeating numbers you can reduce the range and use modulus.
int n = 0, max = 4;
Random rand = new Random();
for(int i = 0; i < numbers; i++) {
n = (n + rand.nextInt(max-1)) % max;
int numToUse = n + 1;
// use this number.
}
This work as there is really only max-1 possible values as you are excluding the last value used.
Here is an algorithm:
initialize an array A[4] with the numbers 1-4
set a counter Acnt, the effective size of A. Initialize to 4
for i in 1 to length(output sequence)
choose a random integer X from 0 to Acnt -1
save A[X] to your output sequence
swap(A[X],A[Acnt - 1])
Acnt--
if(Acnt == 0) Acnt = lengh(A)
Imagine A is a bag of balls with the numbers 1-4. Each iteration of your loop, you remove a ball. Rather than actually deleting from the array, which is expenisve, you simply hide the ball at the end of the array. When you decrement the number of balls in the bag (Acnt), the next ball you select comes from the non-hidden balls.
When you have no more balls to select, you unhide the balls by resetting the count of balls in your bag back to the full count.
This is basically the standard shuffle algorithm.
Edit: Rereading the question, I see now he allows repeats after only 1 number instead of the whole sequence, in which case it all you need to do to modify this is change if (Acnt == 0) to if(Acnt == length(A) - 1).