I had my first casino experience about a month ago. Brought $100 bucks knowing I would lose it. I did, but I absolutely enjoyed the experience. Sat down at a blackjack table not even knowing all the rules and played for about 6 hours. I've since learned basic strategy and hi-lo count basics. I've read Beat the Dealer, started Professional Blackjack, and just picked up The World's Greatest Blackjack Book on the way home at the used bookstore.
I've begun coding a simulator to test out my betting spread and strategy adjustments, but I don't have a whole lot of experience with coding simulators, especially efficient data structures. Could someone please point me in the right direction when it comes to shuffling the decks?
Say I'm using a random number generator to select cards to simulate shuffling. If the generator returns a 4, but I'm out of fours, will just generating a new random number work, or do I need to handle this situation another way? I think we can consider the random number generation an independent event, so it shouldn't matter. I feel like I'm having trouble conveying my confusion.
Is there a difference statistically in the two following scenarios:
Program is in the process of shuffling the shoe.
a)My shuffle function returns a 4, but I'm out of 4s. I proceed to get another random number and a 6 is chosen and I continue.
b) I explicitly keep the program from being able to return a 4, and it generates a 6.
Also, is the Java random number generator sufficient? It didn't seem like the shuffling being done at the casino was that extensive? Has there been any work done to examine the impact of this?
Thanks in advance. Hopefully I can provide some substance in the long run to the forum.
Here is the algorithm for the Fisher-Yates shuffle:
First, generate a discrete random number ranging from 0 to 51 (both limits included!), call it x0.
Exchange the two cards at the array index 0 and x0.
Now, generate another discrete random number, this time ranging from 1 to 51, call it x1.
Again, exchange the two cards at the array index 1 and x1.
Generate x2 ranging from 2 to 51 - exchange the cards at 2 and x2.
...
up to the trivial last card (i.e. x51 ranging from 51 to 51), exchange card at 51 and x51 (=51).
Basically you move through your deck, and at each position you exchange the current card randomly among the remaining card (including itself), then you proceed to the next card in the deck.
Edit: Your algorithms do not work, because it will not reflect the card distribution of the deck or shoe. Say (by previous draws) all but one Ace is removed from the shoe. With your method, drawing the Ace has the same probability as a fresh shoe - which is certainly not true. More worse: Counting systems are explicitly using the cards probability imbalance - which you ironically fail to reproduce with your algorithms.
I was just using a quick scenario. How I have it going is to put 6 decks into an array(just populate it with 2s in spot 0-23 and so on), choose a random number and move that card into another structure. So basically a random number of four would take spot 4 in the array and put it into an array or linked list. If 4 came up again, it would be null and I would ignore it and try again.
a dereference, read and compare action. The tries get worse if you need deep penetration into the shoe.
For my blackjack simulations I just shuffle the array, and keep a pointer at the beginning. For each card drawn I just read the card at the pointers position, and advance the pointer. As I understand your question, you are interested in efficiency. It cannot be more efficient than that (well you can do the Fisher-Yates exchange step each time you "draw" a card if you like).
Anyway, it would be a lot less time wasting, if you would simply say you don't care about answers to your question in the first place.
I wasn't aware it was called the Fisher-Yates shuffle. Thanks. But yes, I learned a long time ago (when I first became interested in programming, about 25 years ago) that was the best way to shuffle a deck of cards.
Make ONE pass through the array
Swap each successive card with a random card
Done.
Note that, at times, the random card chosen could turn out to be the "successive" card... the card you're about to swap. (Thus, you're swapping a card with itself, so in effect, no swap will take place that turn. That's not only okay, it's required and necessary to make sure that all arrangements are possible.
Since suits are irrelevant in blackjack, here's your code to initialize a deck of cards, in BASIC.
number_of_decks = 1
number_of_cards = 52 * number_of_decks
DIM My_Deck(1 to number_of_cards)
FOR suit = 1 to 4
FOR rank = 1 to 13
x = x + 1
My_Deck(x) = rank
NEXT rank
NEXT suit
The above assigns a value of 1 to 13, four times, to your "My_Deck()" array.
For a six deck shoe, this would work:
number_of_decks = 6
number_of_cards = 52 * number_of_decks
DIM My_Deck(1 to number_of_cards)
FOR deck = 1 to 6
FOR suit = 1 to 4
FOR rank = 1 to 13
x = x + 1
My_Deck(x) = rank
NEXT rank
NEXT suit
NEXT deck
Now let's shuffle the cards:
FOR x = 1 to number_of_cards
rnd_number = RND(1, number_of_cards)
SWAP My_Deck(x), My_Deck(rnd_number)
NEXT x
That's it. Again, one pass through the entire array, swapping each successive card with a random card.
So yes, once your cards are shuffled, you no longer should be generating any more random numbers. You just keep track of what number card in the deck you are on, and use that as your pointer to your deck array. The tenth card in the deck is the value of My_Deck(10). The 11th card is the value of My_Deck(11), etc.
1 INTEGER or FIXED declared
2 a$ = "01020304...5152":b$="{104 blanks}":rem 104 characters initialized in order
3 for mix=1 to 3
4 for x=52 to 2 step -1
5 j=rnd(1 to x):b$(105-2x to 105-(2x-1))=a$(2j-1 to 2j)
6 a$=a$(1 to 2j-2)&a$(2j+1 to 2x)
7 next x
8 b$(103 to 104)=a$: rem simplifies error handling, put the last number at the end of b$
9 a$=b$:b$="{104 blanks}":rem transfer the random list b$ to a$ and empty b$
10 next mix
11 for j=1 to 52
12 print a$(2j-1 to 2j);
13 next j:rem proof steps
Essentially this programed can be linked to a small picture 01.gif to 52.gif that can display a representation of the card using the "as" function.
But I left it in its original form.
Quote: s2dbakerHere is what I do. I assign a random number between 0 and 1 to each card in the deck and then sort on that number. In the rare but possible instance that I have a duplicate number, I throw out the whole deck and shuffle again.
This is a slower shuffle than F-Y. O(n log n) vs linear - F-Y requires no sort.
Quote: EdCollins
FOR x = 1 to number_of_cards
rnd_number = RND(1, number_of_cards)
SWAP My_Deck(x), My_Deck(rnd_number)
NEXT x
Almost...
http://www.cigital.com/papers/download/developer_gambling.php
Quote: s2dbakerHere is what I do. I assign a random number between 0 and 1 to each card in the deck and then sort on that number.
Although the sorting makes it slower (as MathExtremist has pointed out), and you are vulnerable to collusions, this shuffle has some benefits: you can make a pretty good model for a riffle.
How to make a riffle shuffle:
Have a list of (rnd, 0) the size of your number of cards, where rnd is a floating point random number between 0 and 2.
Sort the list on the first slot (rnd).
Traversing in ascending order through your list: discard the integer part of the first slot (i.e. keeping only the fractional part of rnd), and assign to the second slot the card of the ordered deck.
Now sort again on the first slot. The second slot is now a (single) riffle through the entire deck.
Quote: EdCollinsMake ONE pass through the array
Swap each successive card with a random card
Done.
It can't be any random card, as that results in a biased shuffle. It has to be within a shrinking range of cards. A C# implementation of a proper Fisher-Yates shuffle is as follows:
static Random RNG = new Random();
void Shuffle(int[] deck)
{
for (int x = deck.Length - 1; x > 0; x--)
{
int r = RNG.Next(x + 1);
if (r != x)
{
int tmp = deck[x];
deck[x] = deck[r];
deck[r] = tmp;
}
}
}
I'm a database programmer so I try to stay away from arrays when I can simply slap a primary key and an index on a table. My speed bottlenecks are not in the shuffling. My biggest speed bump is determining if a seven card hand is a five card (or more) flush.Quote: MathExtremistThis is a slower shuffle than F-Y. O(n log n) vs linear - F-Y requires no sort.
Quote: s2dbakerMy biggest speed bump is determining if a seven card hand is a five card (or more) flush.
If memory is not much of an issue, why not represent each card as a 5-tupel, given its rank and suit flags, say 7h is (7, 1, 0, 0, 0), while Ks is (13, 0, 0, 1, 0).
Then sum up all 5-tupels. You have a flush whenever one of the last 4 entries of the sum is larger (or equal) 5.
I'm sort of doing that already. The way I have my table set up, each suit can be determined through the modulus function against the number 13. The thing is, once I determine that I have a flush, I have to then check to make sure that those five (or more) cards do not contain a straight. The speed is not an issue but the shuffle is not my bottleneck is all that I'm saying.Quote: MangoJIf memory is not much of an issue, why not represent each card as a 5-tupel, given its rank and suit flags, say 7h is (7, 1, 0, 0, 0), while Ks is (13, 0, 0, 1, 0).
Then sum up all 5-tupels. You have a flush whenever one of the last 4 entries of the sum is larger (or equal) 5.
Quote: MathExtremistQuote: EdCollins
FOR x = 1 to number_of_cards
rnd_number = RND(1, number_of_cards)
SWAP My_Deck(x), My_Deck(rnd_number)
NEXT x
Almost...
http://www.cigital.com/papers/download/developer_gambling.php
I beg to differ.
I'm aware of that article, although I hadn't read it in a few years. Notice within that very article it states, "In short, the algorithm in question never chooses to swap the current card with the last card."
My algorithm DOES do that. As it seems you're aware, it's necessary, to produce all arrangements, as I mentioned initially. In my algorithm above (well, it's not MINE... I first became aware of it 20 odd years ago) the random number that is generated could very well swap the current card with the last card. (And it will 1/x of the time, with x being the number of cards in the deck.)
As further proof, to verify I know what I'm talking about, I just wrote a little program that shuffles a three card deck one billion times, using my method. As you know, with just three cards, there are only six possible arrangements: 1-2-3, 1-3-2, 2-1-3, 2-3-1, 3-1-2, 3-2-1. A proper shuffle will produce an equal distribution of these arrangements. Here is the output of my program:
123 = 166683001
% = .166683001
---------------
132 = 166663730
% = .16666373
---------------
213 = 166669360
% = .16666936
---------------
231 = 166667954
% = .166667954
---------------
312 = 166664891
% = .166664891
---------------
321 = 166651064
% = .166651064
---------------
As shown above, after the shuffling process was complete I kept track of the number of times each resulting arrangement occurred. As you can see, each possible arrangement occurs 1/6 of the time. (The differences, of course, are within normal bounds.)
See my post above. It appears my shuffle algorithm then, isn't a "Fisher-Yates Shuffle," but my shuffle DOES produce unbiased shuffles.
Interesting that even after one billion shuffles, the bad distribution isn't "readily" apparent. The results I posted DID use the improper shuffling algorithm... and definitely appear to produce a result of 1/6 for each. But further runs show that 132, 231, and 213 aleasy appear very slightly more often than 123, 312, and 321.
I learned something today. Thanks again.
Quote: EdCollinsAh, my mistake. I stand corrected. Thanks. I see now that the shuffle algorithm I often use is definitely identical to "Problem Two: Bad Distribution Of Shuffles" at the website you posted.
Interesting that even after one billion shuffles, the bad distribution isn't "readily" apparent. The results I posted DID use the improper shuffling algorithm... and definitely appear to produce a result of 1/6 for each. But further runs show that 132, 231, and 213 aleasy appear very slightly more often than 123, 312, and 321.
I learned something today. Thanks again.
Sure, but it strikes me that the results of a biased shuffle of three items should be way more skewed than that. 4/27 and 5/27 are pretty far away from 1/6. I wonder if you don't have modulo bias in your RNG scaling algorithm that's canceling out the shuffle bias... How do you map an RNG call to the range [1..3]?
Quote: MathExtremistSure, but it strikes me that the results of a biased shuffle of three items should be way more skewed than that. 4/27 and 5/27 are pretty far away from 1/6. I wonder if you don't have modulo bias in your RNG scaling algorithm that's canceling out the shuffle bias... How do you map an RNG call to the range [1..3]?
The reason why it wasn't more skewed was because my program/coding was "wrong."
Instead of starting each trial all over again, with a beginning deck of "123" it simply started where the last shuffle left off, whatever that happened to be. I wasn't resetting it at all. So that altered it enough that the end result looked "okay."
Once I fixed the code, but left in the improper shuffle, it clearly showed, after one billion shuffles, that 132, 213, and 231 appeared FAR more often. And then once I implemented the proper shuffle, everything is MUCH closer to 1/6 for all six possible results.
Thanks.
For dice, which is a fully independent trial, one must randomly pick one outcome from the final order of possibilities. As such the variable k will pick one outcome from a randomly mixed and ordered string array. Note that in the original post mix was held constant, and can be improved by allowing a random range of mix-ing.
1 INTEGER or FIXED declared
2 a$ = "111221133114411551166122233224422552266233344335533663444554466455566566":b$=" ":rem 72 characters
3 for mix= rnd(1 to 5)
4 for x=36 to 2 step -1
5 j=rnd(1 to x):b$(73-2x to 73-(2x-1))=a$(2j-1 to 2j)
6 a$=a$(1 to 2j-2)&a$(2j+1 to 2x)
7 next x
8 b$(71 to 72)=a$: rem simplifies error handling, put the last number at the end of b$
9 a$=b$:b$=" ":rem transfer the random list b$ to a$ and empty b$
10 next mix
11 k=rnd(1 to 36)
12 print a$(2k-1);"-"; a$(2k)
13 rem print dice roll
The main reason was to do combinatorial and simulation calculations for blackjack and other games. I often copied codes found on the web that do certain tasks (without really understanding all the underlying parts of the code).
I am using the following code for shuffling a deck reffered to as 'Double Sorting' that I found on the Web in the past.
Double Sorting - The secret of Resampling Without Replacement
Double Sorting is the word I used for sorting one array based on the values of the second array. This method is used when you want to get values from of a sample without select the same value twice .
The code is as follows:
For j = 2 To UBound(x)
xTemp = x(j)
yTemp = y(j)
For i = j - 1 To 1 Step -1
If (x(i) <= xTemp) Then GoTo 10
x(i + 1) = x(i)
y(i + 1) = y(i)
Next i
i = 0
10 x(i + 1) = xTemp
y(i + 1) = yTemp
Next j
End Sub
First you put random numbers from 0-1 using excel Rnd to array x.
Then array y numbers (the 52 cards) are sorted based on the order of the array x (which has random numbers).
My questions for the professional programmers.
Is this code accurate? In the sense that provides random and fair shuffles.
Is it efficient? In terms of doing this code in Visual Basic for Excel.
Quote: AceTwo
Is this code accurate? In the sense that provides random and fair shuffles.
Is it efficient?
I didn't check the code itself. But what you verbally describe is the same algorithm described by s2dbaker earlier in this thread.
Yes it is a random and fair (i.e. unbiased) shuffle. However as already been mentioned, it is not the most efficient, because the sort makes it slow.
VBA in Excel is fine by itself for other reasons, but don't expect top-notch efficient speed.
Quote: MangoJI didn't check the code itself. But what you verbally describe is the same algorithm described by s2dbaker earlier in this thread.
Yes it is a random and fair (i.e. unbiased) shuffle. However as already been mentioned, it is not the most efficient, because the sort makes it slow.
VBA in Excel is fine by itself for other reasons, but don't expect top-notch efficient speed.
Thanks MangoJ. Altough I would like to be able to write efficient code, my most important thing is to be able to write accurate code so as to get accurate results in my sims.
The code that I write and the sims that I do mostly relate to BJ rules that no commercial software analyses (rule variations outside the US) or other games outside the US where no software exists to analyse.