Randomizing Playing Deck - java

I know there are multiple ways of writing code for this type of question, but I'm trying to understand my Professor's way. He wrote a method that takes the value of an integer in an array and assigns it the value of another random integer in the array, i.e "shuffling a deck":
static void shuffle(int[ ] deck) {
//Randomize the order of the elements of deck
//Pick a random card to go in position 0, then position 1, etc.
for(int cardNum=0; cardNum<DECK_SIZE-1; cardNum++){
//pick a random value randomCardNum from cardNum...DECK_SIZE-1
int randomCardNum = cardNum+(int)(Math.random()*(DECK_SIZE-cardNum));
//Swap card and randomCard
.....
What I can't understand is why he would have the for loop go until DECK_SIZE - 1. There are 52 cards, and I know that an array's last index is n-1, but the last cardNum is already not inclusive, so it's going from 0 to 50. I tried taking the -1 out, I get 52 random cards either way.
I'm not sure if it has to do with int randomCardNum, but this seems right as the equation for randomizing numbers in a specific range is :
Min + (int)(Math.random() + (Max-Min))

Your best bet is to ask your professor, but note that if cardNum could go all the way to 51 (the last index), then this line:
int randomCardNum = cardNum+(int)(Math.random()*(DECK_SIZE-cardNum));
...is guaranteed to result in a 51 in randomCardNum on the last iteration, because DECK_SIZE-cardNum will be 1, and so multiplying it by Math.random() will give you a value less than 1, and so casting that value to int will result in 0, and of course cardNum+0 is cardnum.
Since cardNum and randomCardNum would both be 51 on that last iteration, and there's no point in swapping a card with itself, he stopped one iteration early.
My question would have been: Why use DECK_SIZE rather than deck.length, since it introduces a possible maintenance error (changing the size of deck but not remembering to change the constant).

Your professor's code is correct. Look at the random call:
int randomCardNum = cardNum+(int)(Math.random()*(DECK_SIZE-cardNum));
When cardNum starts at 0, it chooses one of 52 cards, puts that random one at position 0. Next time through the loop it chooses one of the 51 remaining and puts it next. Then it chooses one of 50 remaining and puts it next, etc. The last time through the loop there are two cards remaining, it chooses one to be first, and then we're done. You add another iteration to the loop it just selects one of the remaining one card, and places it where it already is, to no effect whatsoever.

Related

When to use set and add methods in list

cutDeck() does the last step of shuffling -- cutting the deck. This should move the first cutLocation number of cards ("first" meaning at the lowest index) from cards and add them to the back of the List. Make certain that you add cards in the same (relative) order that they were originally stored.
public List<PlayingCard> cutDeck(int cutLocation) {
for (int i = 0; i < cutLocation; i++) {
cards.add(cards.get(i));
}
for (int i = 0; i < cutLocation; i++) {
cards.remove(cards.get(i));
}
return cards;
}
This is the error message i am getting
cutDeck(133) did not correctly cut when cards had 208 cards. At index 0 expected 5H but was 3C expected:<[5H]> but was:<[3C]>
I can't see how I am doing something wrong, is it my logic should I be using the cards.set() instead of cards.add()?
Your removal is wrong. Imagine cards has five cards in it and we're splitting it after the first two cards.
1 2 3 4 5
After your first loop terminates, you get:
1 2 3 4 5 1 2
Now, your second loop starts and removes the first card at i = 0, which is 1
2 3 4 5 1 2
Now, your second loop removes the first card at i = 1, which is 3, and NOT 2!
This is where your mistake is. You could simplify the whole logic to a single for loop:
for(int i = 0; i < cutLocation; i++) {
cards.add(cards.remove(0));
}
When you removed the first card in your second for loop all your cards will have been shifted to the front by one position. But your loop variable i is still increasing and you use it in your cards.get(i) call. This means that when you are in the second iteration of your loop you will call cards.get(1), but this would be the original third card to remove. After that you try to remove the next card with cards.get(2), but it would delete the original fifth card and so on.
You might want to use cards.removeAt(0) to (always) remove the card at the beginning until you reached the limit cutLocation.

Java - how to divide an integer into 5 random parts

I'm working on an android graphics app, and at some point in the code, I need to divide lets say, a rectangle's width into 5 random sizes.
I have my randomintegerfunction(int min, int max) in my activity, but that can help me divide it into 2 parts.
How do I go about dividing an integer, lets say 100, into 5 random parts, so that the first one or two parts arent always the biggest, then I subdivide for the third, fourth and fifth parts?
Right now, I am know I can try to implememt it using my random integer generator,but the issue, I think is that I'd have to use some forced divisions, like dividing the first 70% of the integer into 2 parts, then dividing the remaining 20% into two parts, to make a total of 5 parts, but such a method would always make the first part be bigger than the fifth part, which I'd like to avoid...to make it truly random.
What I'd like, for example...
the first part to potentially be 7,
second part 25,
third part 5,
fourth part 40,
fifth/last/remaining part 23. To add up to 100 (or any integer).
I am not sure about how to write the logic of such a function...so please if you have any ideas of how to implement a function that randomly divides an integer into 3 or 4 or 5 or 6 truly random sizes/parts, please enlighten me!
Thanks for your time!
You could randomly select from the amount remaining.
int[] nums = new int[5];
int total = 100;
Random rand = new Random();
for (int i = 0; i < nums.length-1; i++) {
nums[i] = rand.nextInt(total);
total -= nums[i];
}
nums[nums.length-1] = total;
Arrays.sort(nums);
This will select a random number and ensure the sum is always the same. The last sort ensures they are in ascending order.
A simple algorithm is to put the numbers 1-99 into a list, shuffle them, and take the first 4 elements as your "split points", i.e. positions at which to divide the number range.
List<Integer> splitPoints =
IntStream.rangeClosed(1, 99)
.boxed().collect(Collectors.toList());
Collections.shuffle(splitPoints);
splitPoints.subList(4, splitPoints.size()).clear();
Collections.sort(splitPoints);
Now, you have 4 randomly-placed split points. The ranges go from:
0 -> splitPoints.get(0)
splitPoints.get(0) -> splitPoints.get(1)
...
splitPoints.get(3) -> 100.
Take four numbers from below range:
4 to n-1
And then divide each number by four .
And fifth number to be n - (sum of other four).
Where n is 100 in the given case..
Again this is one way of implementation and there are hundred of ways to implement it
Hope that helps.
The most efficient way to do this and to keep proper distribution - looks like this.
1) In general cases. You need divide line into N parts.
generate N-1 doubles [0,1], add 0 and 1, and sort them -> x[i] = {0, ..., 1}
N-1 point divide line into N parts -> 0=x[0]..x[1]; x[1]...x[2]; ... x[N]..x[N+1]=1
scale each part to proper size -> len[i] = (x[i+1]-x[i])*total_length
cast to int if needed
2) In case when you need large Objects and small gaps - split you length with desirable proportion, like 70% for objects and 30% for gaps. Or generate it nextDouble(0.2)+0.2 for [0.2,0.4) range for gaps. Then use proposed algorithm twice.

Array Duplicate Efficiency Riddle

Recently in AP Computer Science A, our class recently learned about arrays. Our teacher posed to us a riddle.
Say you have 20 numbers, 10 through 100 inclusive, right? (these numbers are gathered from another file using Scanners)
As each number is read, we must print the number if and only if it is not a duplicate of a number already read. Now, here's the catch. We must use the smallest array possible to solve the problem.
That's the real problem I'm having. All of my solutions require a pretty big array that has 20 slots in it.
I am required to use an array. What would be the smallest array that we could use to solve the problem efficiently?
If anyone could explain the method with pseudocode (or in words) that would be awesome.
In the worst case we have to use an array of length 19.
Why 19? Each unique number has to be remembered in order to sort out duplicates from the following numbers. Since you know that there are 20 numbers incoming, but not more, you don't have to store the last number. Either the 20th number already appeared (then don't do anything), or the 20th number is unique (then print it and exit – no need to save it).
By the way: I wouldn't call an array of length 20 big :)
If your numbers are integers: You have a range from 10 to 100. So you need 91 Bits to store which values have already been read. A Java Long has 64 Bits. So you will need an array of two Longs. Let every Bit (except for the superfluous ones) stand for a number from 10 to 100. Initialize both longs with 0. When a number is read, check if the corresponding bit mapped to the read value is set to 1. If yes, the read number is a duplicate, if no set the bit to 1.
This is the idea behind the BitSet class.
Agree with Socowi. If number of numbers is known and it is equal to N , it is always possible to use N-1 array to store duplicates. Once the last element from the input is received and it is already known that this is the last element, it is not really needed to store this last value in the duplicates array.
Another idea. If your numbers are small and really located in [10:100] diapason, you can use 1 Long number for storing at least 2 small Integers and extract them from Long number using binary AND to extract small integers values back. In this case it is possible to use N/2 array. But it will make searching in this array more complicated and does not save much memory, only number of items in the array will be decreased.
You technically don't need an array, since the input size is fixed, you can just declare 20 variables. But let's say it wasn't fixed.
As other answer says, worst case is indeed 19 slots in the array. But, assuming we are talking about integers here, there is a better case scenario where some numbers form a contiguous interval. In that case, you only have to remember the highest and lowest number, since anything in between is also a duplicate. You can use an array of intervals.
With the range of 10 to 100, the numbers can be spaced apart and you still need an array of 19 intervals, in the worst case. But let's say, that the best case occurs, and all numbers form a contiguous interval, then you only need 1 array slot.
The problem you'd still have to solve is to create an abstraction over an array, that expands itself by 1 when an element is added, so it will use the minimal size necessary. (Similar to ArrayList, but it doubles in size when capacity is reached).
Since an array cannot change size at run time You need a companion variable to count the numbers that are not duplicates and fill the array partially with only those numbers.
Here is a simple code that use companion variable currentsize and fill the array partially.
Alternative you can use arrayList which change size during run time
final int LENGTH = 20;
double[] numbers = new double[LENGTH];
int currentSize = 0;
Scanner in = new Scanner(System.in);
while (in.hasNextDouble()){
if (currentSize < numbers.length){
numbers[currentSize] = in.nextDouble();
currentSize++;
}
}
Edit
Now the currentSize contains those actual numbers that are not duplicates and you did not fill all 20 elements in case you had some duplicates. Of course you need some code to determine whither a numbers is duplicate or not.
My last answer misunderstood what you were needing, but I turned this thing up that does it an int array of 5 elements using bit shifting. Since we know the max number is 100 we can store (Quite messily) four numbers into each index.
Random rand = new Random();
int[] numbers = new int[5];
int curNum;
for (int i = 0; i < 20; i++) {
curNum = rand.nextInt(100);
System.out.println(curNum);
boolean print = true;
for (int x = 0; x < i; x++) {
byte numberToCheck = ((byte) (numbers[(x - (x % 4)) / 4] >>> ((x%4) * 8)));
if (numberToCheck == curNum) {
print = false;
}
}
if (print) {
System.out.println("No Match: " + curNum);
}
int index = ((i - (i % 4)) / 4);
numbers[index] = numbers[index] | (curNum << (((i % 4)) * 8));
}
I use rand to get my ints but you could easily change this to a scanner.

Why does the output for my program always start with two consecutive zeros even though I wrote a code that doesn't produce zeros or repeated numbers?

I am trying to create a method that produces lottery numbers with a random number generator. There are two groups of numbers. Group one is supposed to have five different numbers that appear in sorted order, with a range of 1-56. Group two consists of a single number with a range of 1-46. When I run the program, group one always begins two consecutive zeros even though I tried to write the code in a way that doesn't allow group one to have zeros or repeating numbers. At first I thought that the problem must have something to do with the random number generator, so I tried debugging the project in NetBeans. As I stepped through the lines of code, I could see the values assigned to n, which is the variable that holds the numbers produced by the random generator. The values of n were 54, 50, 11, 49, and 28. In the program, the values of n are put into a sorted array. So the output for group one should have been 11, 28, 49, 50, 54, but instead it was 0, 0, 11, 28, 49.
Here is my code:
public static void MakeTickets(){
int [] Group1= new int [5];
int Group2;
int n;
Random rand= new Random ();
for (int j=0 ; j<5; j++){
//Here, I try to make sure that the range for n is 1-56
n= rand.nextInt(55)+1;
//Here, I try to make sure that a number isn't put into the group one
//array more than once
while (Arrays.binarySearch(Group1, n)>=0){
n= rand.nextInt(55)+1;
}
Group1[j]=n;
Arrays.sort(Group1);
}
Random r= new Random();
int num= r.nextInt(45)+1;
Group2=num;
System.out.print("Here is your ticket: Group One= ");
for(int number: Group1){
if (number==Group1[4]){
System.out.print(number);
} else {
System.out.print(number+", ");
}
}
System.out.println(" Group Two= "+Group2);
}
Here is the output:
Here is your ticket: Group One= 0, 0, 33, 45, 50 Group Two= 40
I've tried using ThreadLocalRandom instead, but I still had the same problem. Does anyone know what I am doing wrong? Any and all advice is much appreciated.
The Arrays.sort(Group1); is causing the problem.
I believe the Arrays.sort(Group1); should be placed after the first for loop (after generating the Group1 values).
Currently the values are sorted after adding every value.
Initially the values in the array are 0 0 0 0 0
1st Iteration
After generating the first number
n= rand.nextInt(55)+1; //lets assume the generated value is 43
the, array becomes 43 0 0 0 0
After calling the sort ,
Arrays.sort(Group1);
the array becomes 0 0 0 0 43
2nd Iteration.
After generating the second number
n= rand.nextInt(55)+1; //lets assume the generated value is 22
the, array becomes 0 22 0 0 43
After calling the sort ,
Arrays.sort(Group1);
the array becomes 0 0 0 22 43
3rd Iteration
After generating the third number
n= rand.nextInt(55)+1; //lets assume the generated value is 31
the, array becomes 0 0 31 22 43
After calling the sort ,
Arrays.sort(Group1);
the array becomes 0 0 22 31 43
The 4th and 5th iteration do not change the first two values in the array. This way the first 2 number get stuck as 0s, which explains your result.
You can't use Arrays.binarySearch on an unsorted array. In such case result of this operation is unpredictable.
Sort moves all zeros to the beginning of the array. But you are not rewriting them (cause j is being incremented).
Hello there,As far as I know, there are zero-digit situation, the general is the default allocation of zero is the compiler, that you did not initialize this variable.
I believe the problem is that the int array is being sorted every time. The int array is initialized with values of 0. By sorting the array every time you are changing where the new random number needs to be inserted. So sometimes you are accidentally overwriting one of the randomly generated numbers instead of the 0's.
Perhaps instead of using BinarySearch which requires the sorting just used contains and get rid of the sorting until all of the random numbers are generated. I don't know the exact syntax since I haven't coded in Java for 3 years, but you could do something like Arrays.asList(Group1).contains(n). So...
for (int j = 0; j < 5; j++) {
//Here, I try to make sure that the range for n is 1-56
n= rand.nextInt(55)+1;
while (Arrays.asList(Group1).contains(n)) {
n = rand.nextInt(55)+1;
}
Group1[j] = n;
}
Arrays.sort(Group1);

Shuffling a Deck of Cards , Redundancy after swapping two values

I was asked to write a program(mainly a method) for shuffling a deck of cards. I wrote the following program:
public class Deck {
////////////////////////////////////////
// Data Members
////////////////////////////////////////
private Card[] cards; // array holding all 52 cards
private int cardsInDeck; // the current number of cards in the deck
public static final int DECK_SIZE = 52;
/**
* Shuffles the deck (i.e. randomly reorders the cards in the deck).
*/
public void shuffle() {
int newI;
Card temp;
Random randIndex = new Random();
for (int i = 0; i < cardsInDeck; i++) {
// pick a random index between 0 and cardsInDeck - 1
newI = randIndex.nextInt(cardsInDeck);
// swap cards[i] and cards[newI]
temp = cards[i];
cards[i] = cards[newI];
cards[newI] = temp;
}
}
}
But there is a logical error in the above shuffle method which is as follows: Suppose I replace Card Number 4 with Card Number 42, then I'm swapping
two times. I'm wondering is there any way of not doing this?
I checked one post here :Shuffling a deck of cards
But it didn't make sense to me.
I'm wondering is there any way of not doing this?
Absolutely. Instead of swapping one card with any other, simply swap one card with a later one.
So at any point, you're really picking which card is going to be in slot i from "all the remaining cards" which haven't been picked. It's conceptually equivalent to starting with one list of cards, and removing cards at random to place in the new shuffled collection. The fact that you're actually swapping locations while you're doing that is irrelevant, as at any point you'll be picking uniformly randomly from the remaining slots.
Read the Wikipedia article on the Fisher-Yates shuffle for more information.
(Some implementations swap from the end, so element x is swapped with a random element in the range [0, x]. That's equivalent to what I described, just mirrored. Personally I find it easier to think of the first part of the collection as being the shuffled part at any point, but that's a failing on my part rather than an inherent difference.)
Also bear in mind that if you use a List<Card>, you can use Collections.shuffle and avoid having to write the code for this at all.
You can compare your implementation with Collections.shuffle, that one definitely works right, this is a snippet from src
// Shuffle array
for (int i=size; i > 1; i--)
swap(arr, i-1, rnd.nextInt(i));
...
private static void swap(Object[] arr, int i, int j) {
Object tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
Sometimes, the best way to shuffle a deck is to not shuffle it. Since you already know how to use random numbers, you can use a modification to the Fisher-Yates shuffle to extract cards in random order, without duplicates, all without an initial sort.
Think of it in these physical terms (when using a real deck of cards). Rather than shuffling the deck up front then continuously extracting the top card, just leave the deck in sorted order and extract a card from a random location each time.
See here for an complete explanation of how this works but I'll cover extracting three numbers from 1 through 9 inclusive below.
Start with the (unshuffled) list {1,2,3,4,5,6,7,8,9} (of length 9, obviously) and generate a random number based on that length (from 0 to 8 inclusive, assuming we use zero-based indexes, which Java does). Let's say the first random number is 4.
You then save the item at position number 4 (which is 5) and move the _last item in the list (9) to that position, decreasing the length by one. That gives you {1,2,3,4,9,6,7,8} with a length of 8.
Then go back for a second number again using a random number based on the length (0 through 7 inclusive). In this case, we'll get the random number 1.
The item at offset 1 is 2 and we then adjust the list same as the first step, giving {1,8,3,4,9,6,7} with a length of 7.
Now let's say we get a third random number based on the current length of 7 and it happens to be 4 again. That item is now 9 so we return that, after modifying the list to become {1,8,3,4,7,6} with length 6.
You should be able to see how this is developing. Without any worry about sorting the entire list up front, you can achieve a random sequence (well, as random as your random number generator permits) without repeats.

Categories