Smartpart
Smartpart
  • Threads: 2
  • Posts: 8
Joined: Nov 3, 2012
November 3rd, 2012 at 2:19:59 PM permalink
Hello everyone.

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.
MangoJ
MangoJ
  • Threads: 10
  • Posts: 905
Joined: Mar 12, 2011
November 3rd, 2012 at 2:42:26 PM permalink
The simplest non-biased shuffle algorithm is the Fisher-Yates shuffle. Lets assume you have a deck (an array) of 52 cards, index ranges from 0 to 51.

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.
Smartpart
Smartpart
  • Threads: 2
  • Posts: 8
Joined: Nov 3, 2012
November 3rd, 2012 at 3:10:55 PM permalink
Thanks for telling me about Fisher-Yates.

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.
MangoJ
MangoJ
  • Threads: 10
  • Posts: 905
Joined: Mar 12, 2011
November 3rd, 2012 at 4:25:47 PM permalink
With your method, you need an undetermined number of "tries" for each card drawn. Each try consumes calculation of a new random number,
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.
Smartpart
Smartpart
  • Threads: 2
  • Posts: 8
Joined: Nov 3, 2012
November 3rd, 2012 at 5:03:52 PM permalink
This is exactly what I was looking for. It seems I came across as arrogant, and I apologize because I didn't mean it that way. I was simply struggling to explain my confusion. I care about the answer, and I truly appreciate your help, Mango.
s2dbaker
s2dbaker
  • Threads: 51
  • Posts: 3259
Joined: Jun 10, 2010
November 3rd, 2012 at 8:55:20 PM permalink
Here 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.
Someday, joor goin' to see the name of Googie Gomez in lights and joor goin' to say to joorself, "Was that her?" and then joor goin' to answer to joorself, "That was her!" But you know somethin' mister? I was always her yuss nobody knows it! - Googie Gomez
EdCollins
EdCollins
  • Threads: 20
  • Posts: 1739
Joined: Oct 21, 2011
November 3rd, 2012 at 10:21:14 PM permalink
MangoJ nailed it.

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.
98Clubs
98Clubs
  • Threads: 52
  • Posts: 1728
Joined: Jun 3, 2010
November 3rd, 2012 at 11:04:09 PM permalink
From the WayBack machne ca. 1976 and upgraded 10 years later using "SuperBasic"...

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.
Some people need to reimagine their thinking.
MathExtremist
MathExtremist
  • Threads: 88
  • Posts: 6526
Joined: Aug 31, 2010
November 3rd, 2012 at 11:51:29 PM permalink
Quote: s2dbaker

Here 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.
"In my own case, when it seemed to me after a long illness that death was close at hand, I found no little solace in playing constantly at dice." -- Girolamo Cardano, 1563
MathExtremist
MathExtremist
  • Threads: 88
  • Posts: 6526
Joined: Aug 31, 2010
November 3rd, 2012 at 11:56:26 PM permalink
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
"In my own case, when it seemed to me after a long illness that death was close at hand, I found no little solace in playing constantly at dice." -- Girolamo Cardano, 1563
MangoJ
MangoJ
  • Threads: 10
  • Posts: 905
Joined: Mar 12, 2011
November 4th, 2012 at 1:36:45 AM permalink
Quote: s2dbaker

Here 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.
JB
Administrator
JB
  • Threads: 334
  • Posts: 2089
Joined: Oct 14, 2009
November 4th, 2012 at 3:56:39 AM permalink
Quote: EdCollins

Make 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;
}
}
}
s2dbaker
s2dbaker
  • Threads: 51
  • Posts: 3259
Joined: Jun 10, 2010
November 4th, 2012 at 6:29:40 AM permalink
Quote: MathExtremist

This is a slower shuffle than F-Y. O(n log n) vs linear - F-Y requires no sort.

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.
Someday, joor goin' to see the name of Googie Gomez in lights and joor goin' to say to joorself, "Was that her?" and then joor goin' to answer to joorself, "That was her!" But you know somethin' mister? I was always her yuss nobody knows it! - Googie Gomez
MangoJ
MangoJ
  • Threads: 10
  • Posts: 905
Joined: Mar 12, 2011
November 4th, 2012 at 6:49:32 AM permalink
Quote: s2dbaker

My 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.
s2dbaker
s2dbaker
  • Threads: 51
  • Posts: 3259
Joined: Jun 10, 2010
November 4th, 2012 at 6:58:16 AM permalink
Quote: MangoJ

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.
Someday, joor goin' to see the name of Googie Gomez in lights and joor goin' to say to joorself, "Was that her?" and then joor goin' to answer to joorself, "That was her!" But you know somethin' mister? I was always her yuss nobody knows it! - Googie Gomez
EdCollins
EdCollins
  • Threads: 20
  • Posts: 1739
Joined: Oct 21, 2011
November 4th, 2012 at 9:28:18 AM permalink
Quote: MathExtremist

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



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.)
EdCollins
EdCollins
  • Threads: 20
  • Posts: 1739
Joined: Oct 21, 2011
November 4th, 2012 at 9:31:25 AM permalink
Re: "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:..."

See my post above. It appears my shuffle algorithm then, isn't a "Fisher-Yates Shuffle," but my shuffle DOES produce unbiased shuffles.
MathExtremist
MathExtremist
  • Threads: 88
  • Posts: 6526
Joined: Aug 31, 2010
November 4th, 2012 at 9:51:36 AM permalink
You don't need to simulate to show bias. You're right in that there are only six permutations of three cards, but the algorithm you posted produces nine (whoops, 27) different shuffles, not six. If you simulated and got 1/6 for everything, you didn't use the loop you posted.
"In my own case, when it seemed to me after a long illness that death was close at hand, I found no little solace in playing constantly at dice." -- Girolamo Cardano, 1563
EdCollins
EdCollins
  • Threads: 20
  • Posts: 1739
Joined: Oct 21, 2011
November 4th, 2012 at 10:58:41 AM permalink
Ah, 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.
MathExtremist
MathExtremist
  • Threads: 88
  • Posts: 6526
Joined: Aug 31, 2010
November 4th, 2012 at 9:25:33 PM permalink
Quote: EdCollins

Ah, 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]?
"In my own case, when it seemed to me after a long illness that death was close at hand, I found no little solace in playing constantly at dice." -- Girolamo Cardano, 1563
EdCollins
EdCollins
  • Threads: 20
  • Posts: 1739
Joined: Oct 21, 2011
November 4th, 2012 at 10:52:26 PM permalink
Quote: MathExtremist

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]?



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.
98Clubs
98Clubs
  • Threads: 52
  • Posts: 1728
Joined: Jun 3, 2010
November 5th, 2012 at 11:34:24 PM permalink
My above post is valid for card-based games where the final order of cards is dealt "off-the-top" until play stops (Poker) or a stop-card is revealed (21).

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
Some people need to reimagine their thinking.
AceTwo
AceTwo
  • Threads: 5
  • Posts: 359
Joined: Mar 13, 2012
November 6th, 2012 at 5:03:28 AM permalink
I am self-taught in Visual Basic in Excel. I am not really a programmer (never had any formal training) but learned using resources on the web.
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.
MangoJ
MangoJ
  • Threads: 10
  • Posts: 905
Joined: Mar 12, 2011
November 6th, 2012 at 6:25:12 AM permalink
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.
AceTwo
AceTwo
  • Threads: 5
  • Posts: 359
Joined: Mar 13, 2012
November 6th, 2012 at 11:21:13 AM permalink
Quote: MangoJ

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.



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.
  • Jump to: