Wizard
Administrator
Wizard
  • Threads: 1493
  • Posts: 26489
Joined: Oct 14, 2009
October 30th, 2013 at 7:14:39 AM permalink
I've tried to find an answer to this online, but it seems there are endless ways to code a Mersenne Twister, and I can't find commentary to match this code. There are even comments about seeding below, but I still don't see how to do it.

Thanks in advance for your help.


void init_genrand(unsigned long s)
{
mt[0]= s & 0xffffffffUL;
for (mti=1; mti<N; mti++) {
mt[mti] =
(1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
/* In the previous versions, MSBs of the seed affect */
/* only MSBs of the array mt[]. */
/* 2002/01/09 modified by Makoto Matsumoto */
mt[mti] &= 0xffffffffUL;
/* for >32 bit machines */
}
}

/* initialize by an array with array-length */
/* init_key is the array for initializing keys */
/* key_length is its length */
/* slight change for C++, 2004/2/26 */
void init_by_array(unsigned long init_key[], int key_length)
{
int i, j, k;
init_genrand(19650218UL);
i=1; j=0;
k = (N>key_length ? N : key_length);
for (; k; k--) {
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
+ init_key
+ j; /* non linear */
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
i++; j++;
if (i>=N) { mt[0] = mt[N-1]; i=1; }
if (j>=key_length) j=0;
}
for (k=N-1; k; k--) {
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
- i; /* non linear */
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
i++;
if (i>=N) { mt[0] = mt[N-1]; i=1; }
}

mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
}

/* generates a random number on [0,0xffffffff]-interval */
unsigned long genrand_int32(void)
{
unsigned long y;
static unsigned long mag01[2]={0x0UL, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */

if (mti >= N) { /* generate N words at one time */
int kk;

if (mti == N+1) /* if init_genrand() has not been called, */
init_genrand(5489UL); /* a default initial seed is used */

for (kk=0;kk<N-M;kk++) {
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
for (;kk<N-1;kk++) {
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];

mti = 0;
}

y = mt[mti++];

/* Tempering */
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680UL;
y ^= (y << 15) & 0xefc60000UL;
y ^= (y >> 18);

return y;
}
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
Ahigh
Ahigh
  • Threads: 90
  • Posts: 5197
Joined: May 19, 2010
October 30th, 2013 at 7:16:40 AM permalink
You need to fix when code converts subscripts to arrays with an index of i to an italic BBcode.
aahigh.com
Wizard
Administrator
Wizard
  • Threads: 1493
  • Posts: 26489
Joined: Oct 14, 2009
October 30th, 2013 at 7:20:02 AM permalink
Quote: Ahigh

You need to fix when code converts subscripts to arrays with an index of i to an italic BBcode.



An autographed free copy of my book if you just do it for me...please.
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
MathExtremist
MathExtremist
  • Threads: 88
  • Posts: 6526
Joined: Aug 31, 2010
October 30th, 2013 at 7:26:10 AM permalink
It appears you're looking at this code:
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/CODES/mt19937ar.c

The README for that version is here:
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/CODES/readme-mt.txt

It says, in part:
Quote:

init_genrand(seed) initializes the state vector by using
one unsigned 32-bit integer "seed", which may be zero.

init_by_array(init_key, key_length) initializes the state vector
by using an array init_key[] of unsigned 32-bit integers
of length key_kength. If key_length is smaller than 624,
then each array of 32-bit integers gives distinct initial
state vector. This is useful if you want a larger seed space
than 32-bit word.



So either call init_genrand() with a 32-bit uint, or call init_by_array() with an array of up to 624 32-bit uints. (More than 624 will get ignored.)

And the entire website is here:
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/eversions.html
"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
Wizard
Administrator
Wizard
  • Threads: 1493
  • Posts: 26489
Joined: Oct 14, 2009
October 30th, 2013 at 7:33:00 AM permalink
Thanks! I am not that picky. Would it suffice to seed to capture the time at the start of the program, and keep reseeding with that same time?
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
MathExtremist
MathExtremist
  • Threads: 88
  • Posts: 6526
Joined: Aug 31, 2010
October 30th, 2013 at 7:49:45 AM permalink
Quote: Wizard

Thanks! I am not that picky. Would it suffice to seed to capture the time at the start of the program, and keep reseeding with that same time?


System time in milliseconds is often used for a seed, but why reseed often? The period of the MT is 2^19937-1...

Read the paper if you have the interest:
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/ARTICLES/earticles.html
"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
Wizard
Administrator
Wizard
  • Threads: 1493
  • Posts: 26489
Joined: Oct 14, 2009
October 30th, 2013 at 8:28:09 AM permalink
Quote: MathExtremist

System time in milliseconds is often used for a seed, but why reseed often? The period of the MT is 2^19937-1...



Thanks! That occurred to me after I posted. That paper was a bit over my head.
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
Ahigh
Ahigh
  • Threads: 90
  • Posts: 5197
Joined: May 19, 2010
October 30th, 2013 at 8:37:11 AM permalink
Quote: Wizard

Thanks! That occurred to me after I posted. That paper was a bit over my head.



Shuffling that starts from a randomly selected previously shuffled deck in a library instead of a fresh deck also helps to ensure randomness without burning too many clocks.

The library can be continuously updated as you create new decks so that no starting deck is used more than once.

The library can also be quite large and use permanent storage between runs as the storage space needed per deck is quite small.

I believe, though, if you have a good enough random number generator, the simplest shuffle is:

for( i=0; i<ncards; i++ )
{
random_card = unshuffled_deck( random_index( ncards - i ) );
shuffled_deck += random_card;
unshuffled_deck -= random_card;
}

Starting with a randomly created shuffled deck from a big library of shuffled decks doesn't hurt. But I don't think you need to take much time to shuffle if you do it right.

All the time should be in the random number generator IMO.
aahigh.com
AcesAndEights
AcesAndEights
  • Threads: 67
  • Posts: 4300
Joined: Jan 5, 2012
October 30th, 2013 at 8:53:21 AM permalink
Why not just use the internet to get truly random data?

This will generate a random integer between 0 and 99999999. The random bits come from atmospheric noise, which is better than any PRNG. Read more at the homepage of random.org.

Now, you will have to code an HTTP client into your C++ to utilize this source, which might (will) suck. For best results, stop using C++.

Anyway, unless you were looking at the Mersenne Twister as as academic exercise, then carry on :).
"So drink gamble eat f***, because one day you will be dust." -ontariodealer
Wizard
Administrator
Wizard
  • Threads: 1493
  • Posts: 26489
Joined: Oct 14, 2009
October 30th, 2013 at 8:59:20 AM permalink
Quote: Ahigh

I believe, though, if you have a good enough random number generator, the simplest shuffle is:

for( i=0; i<ncards; i++ )
{
random_card = unshuffled_deck( random_index( ncards - i ) );
shuffled_deck += random_card;
unshuffled_deck -= random_card;
}



I'm not saying this is optimal or unbiased, but I run through the deck hundreds of times swapping the cards in two random positions.

Quote: AcesAndEights

The random bits come from atmospheric noise, which is better than any PRNG. Read more at the homepage of random.org.



I it would take me ages to figure out how to grab the numbers from that web site into my program. My own Mersenne Twister has passed every test of randomness I've thrown at it.
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
wudged
wudged
  • Threads: 2
  • Posts: 998
Joined: Aug 7, 2013
October 30th, 2013 at 9:21:20 AM permalink
Have you ever tried writing something to simulate an actual shuffle?

Such as - take 2 stacks of a random number of cards between 20 and 32 each off the top of an 8 deck shoe, then alternate 1-4 cards from each stack into a new stack (for a riffle)

Not that it is any better or worse than what you are doing anyway, but I wonder what some of these RNG doubters would say about it.
Wizard
Administrator
Wizard
  • Threads: 1493
  • Posts: 26489
Joined: Oct 14, 2009
October 30th, 2013 at 9:35:47 AM permalink
Quote: wudged

Have you ever tried writing something to simulate an actual shuffle?



A long time ago I did try to mimic an actual shuffle. However, I think swapping two random cards is less biased and much easier to code.
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
Ahigh
Ahigh
  • Threads: 90
  • Posts: 5197
Joined: May 19, 2010
October 30th, 2013 at 9:59:59 AM permalink
Quote: Wizard

I'm not saying this is optimal or unbiased, but I run through the deck hundreds of times swapping the cards in two random positions.



You need to shuffle each card at least once, IMO. How many swaps between two random cards are required to make sure each card gets shuffled?

With your method, you need more swaps than the number of cards in a deck. But unless you provide an explanation for how many are required, I think there's an underlying assumption that you are doing "enough."

My method provides randomness for each card in the new deck that is 100% random and performs a minimal number of steps.
aahigh.com
Wizard
Administrator
Wizard
  • Threads: 1493
  • Posts: 26489
Joined: Oct 14, 2009
October 30th, 2013 at 10:09:49 AM permalink
Quote: Ahigh

You need to shuffle each card at least once, IMO. How many swaps between two random cards are required to make sure each card gets shuffled?



No number will guarantee every card gets touched. However, if you do 416 swaps, for example, every card should get swapped twice on average. About 13.5% of cards still won't get swapped. However, that doesn't contaminate the shoe. In baccarat, it is groups of cards that count. If I were programming this for real money wagers I would do a lot more swaps, to avoid player's detecting and exploiting anything. However, for my purposes, I think this technique is sufficient.
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
thecesspit
thecesspit
  • Threads: 53
  • Posts: 5936
Joined: Apr 19, 2010
October 30th, 2013 at 10:12:06 AM permalink
AHigh, see :

http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

You only need to pass once through the deck, swapping two cards at a time to end up with a random shuffle. It also does it in place, which is memory efficient, if such a thing matters. The trick is to avoid the newbie mistake and end up with a cyclic shuffle (Sattolo's algorithm).
"Then you can admire the real gambler, who has neither eaten, slept, thought nor lived, he has so smarted under the scourge of his martingale, so suffered on the rack of his desire for a coup at trente-et-quarante" - Honore de Balzac, 1829
Ahigh
Ahigh
  • Threads: 90
  • Posts: 5197
Joined: May 19, 2010
October 30th, 2013 at 10:13:52 AM permalink
Quote: Wizard

No number will guarantee every card gets touched. However, if you do 416 swaps, for example, every card should get swapped twice on average. About 13.5% of cards still won't get swapped. However, that doesn't contaminate the shoe. In baccarat, it is groups of cards that count. If I were programming this for real money wagers I would do a lot more swaps, to avoid player's detecting and exploiting anything. However, for my purposes, I think this technique is sufficient.



I'm surprised with your explanation of "good enough" compared to what I would consider a complete shuffle with no hint of patterns except possibly patterns in the random number generator itself.
aahigh.com
Ahigh
Ahigh
  • Threads: 90
  • Posts: 5197
Joined: May 19, 2010
October 30th, 2013 at 10:17:20 AM permalink
Quote: thecesspit

AHigh, see :

http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

You only need to pass once through the deck, swapping two cards at a time to end up with a random shuffle. It also does it in place, which is memory efficient, if such a thing matters. The trick is to avoid the newbie mistake and end up with a cyclic shuffle (Sattolo's algorithm).



I think efficiency always matters. Both memory and CPU. You just want to get the job done as efficiently as possible providing that it is done properly.

Swapping two random indices just seems wrong to me intuitively.

I only read briefly the wiki entry, but it appears that it also step incrementally through one index swapping with another random index.

I am not an expert here, though, nor am I research on shuffle algorithms. I would trust Knuth on the subject, for sure.

You would end up with as many shuffles as you have seeds with my algorithm, which is why you want to start with a random result from a library. So maybe you have two seeds to prevent someone from reverse engineering your process. One to look up which shuffled deck to start with, and another to use for the shuffle process, and initialize those two using some external random number generator (EG: from atmospheric data or from a natural random number in the real world). But there's no need to burn CPU to get random results is my thinking.
aahigh.com
thecesspit
thecesspit
  • Threads: 53
  • Posts: 5936
Joined: Apr 19, 2010
October 30th, 2013 at 10:20:43 AM permalink
Quote: Ahigh

I think efficiency always matters. Both memory and CPU. You just want to get the job done as efficiently as possible providing that it is done properly.
Swapping two random indices just seems wrong to me intuitively.



Fisher-Yates avoids any correlation between the start and end deck, for sure. Swapping two items in place also seems less effective to me, as the number of swaps is also less in a F-Y than in a random swap.

It's a bit like the various sort algorithms you can use... though the answer is clearer here.

Quote:

I only read briefly the wiki entry, but it appears that it also step incrementally through one index swapping with another random index.



Pretty much, but you can do it in place as well.

Quote:

I am not an expert here, though, nor am I research on shuffle algorithms. I would trust Knuth on the subject, for sure.



I've seen at least one implementation of F-Y that was actually cyclic, which caused bias in the model.
"Then you can admire the real gambler, who has neither eaten, slept, thought nor lived, he has so smarted under the scourge of his martingale, so suffered on the rack of his desire for a coup at trente-et-quarante" - Honore de Balzac, 1829
Wizard
Administrator
Wizard
  • Threads: 1493
  • Posts: 26489
Joined: Oct 14, 2009
October 30th, 2013 at 10:46:58 AM permalink
This is a little off topic, but speaks to the topic of finding the n-th element in an array with lots of gaps in it. For example, I have an array of 416 cards, but half the positions are blank, and I want to grab the 123rd non-blank position. It would be easy to code, but time consuming for the computer, to parse through the array, counting the cards.

My question is, does the computer do this anyway? For example, if you want the 123rd element in a full array, can the computer jump right to that position, or does it start at the beginning and take 122 more steps until it gets to the element requested?

To phrase it yet another way, when you declare an array, are the locations in the computer's memory sequential, or do they jump around? Dealing with pointers, I would tend to think they are sequential, but I don't trust myself to be right on that one.
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
wudged
wudged
  • Threads: 2
  • Posts: 998
Joined: Aug 7, 2013
October 30th, 2013 at 10:54:13 AM permalink
It depends on the programming language, and also whether you are using a true array or some kind of library.

Originally the memory was stored sequentially as you suggest, and that's why arrays are traditionally 0 based.

The array variable would be a pointer to the memory location of the first element, and then there would be a memory offset from that address based on the index of the array you wanted. The first element is located 0 bytes away from the address pointer. For the nth element, it is located n * (1, 2, 4, 8, 16, 32, whatever - depends on the data type of the elements in the array) bytes away from the address pointer.
AcesAndEights
AcesAndEights
  • Threads: 67
  • Posts: 4300
Joined: Jan 5, 2012
October 30th, 2013 at 10:57:09 AM permalink
Quote: Wizard

I it would take me ages to figure out how to grab the numbers from that web site into my program. My own Mersenne Twister has passed every test of randomness I've thrown at it.


Here's a C++ library already written for you. I understand if you want to stick to your Mersenne code - but as a programmer the availability of truly random data from the environment is really "cool."

Quote: Wizard

This is a little off topic, but speaks to the topic of finding the n-th element in an array with lots of gaps in it. For example, I have an array of 416 cards, but half the positions are blank, and I want to grab the 123rd non-blank position. It would be easy to code, but time consuming for the computer, to parse through the array, counting the cards.

My question is, does the computer do this anyway? For example, if you want the 123rd element in a full array, can the computer jump right to that position, or does it start at the beginning and take 122 more steps until it gets to the element requested?

To phrase it yet another way, when you declare an array, are the locations in the computer's memory sequential, or do they jump around? Dealing with pointers, I would tend to think they are sequential, but I don't trust myself to be right on that one.


Now you're speaking my language!

This question has to do with how the computer physically accesses memory locations. With an array, the data is stored as a block of contiguous memory. Accessing the Nth element is constant-time, meaning the processor doesn't have to iterate over all of the elements to get to the Nth. This is because internally, the reference to the array is stored as a memory location. When you ask for the Nth item in the array, the processor adds N*(sizeof(int)) to that memory location to get the data.

Technically this is dependent on the language's implementation of an array, but I know it's true for C and C++. And it should be true of most languages, as far as I know.

EDIT: Wudged beat me to it, and is correct. If you're using a library, all bets are off and it's implementation-specific. But for a primitive array, both of our explanations are correct.
"So drink gamble eat f***, because one day you will be dust." -ontariodealer
Wizard
Administrator
Wizard
  • Threads: 1493
  • Posts: 26489
Joined: Oct 14, 2009
October 30th, 2013 at 11:03:06 AM permalink
Thanks for the last two posts. That is what I thought. I program in C++ whenever I can. Sometimes Visual Basic, Perl, or JavaScript, depending on the purpose.
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
jml24
jml24
  • Threads: 2
  • Posts: 295
Joined: Feb 28, 2011
October 30th, 2013 at 11:04:40 AM permalink
Quote: Wizard

To phrase it yet another way, when you declare an array, are the locations in the computer's memory sequential, or do they jump around? Dealing with pointers, I would tend to think they are sequential, but I don't trust myself to be right on that one.



I am guessing you are talking C++ in which case yes, they are sequential. You can take the address of the array and use that pointer as expected.

On the shuffling topic you should just use Fisher-Yates there is a correct implementation on Wikipedia. The same article talks about the need to avoid modulo bias when scaling random numbers. I seem to recall an early online poker site made this mistake resulting in biased shuffles. The common method to fix this is to treat the output of the RNG as a bitstream, grab the minimum number of bits that can express your desired maximum, and then throw away samples that are out of range.
Ibeatyouraces
Ibeatyouraces
  • Threads: 68
  • Posts: 11933
Joined: Jan 12, 2010
October 30th, 2013 at 11:05:28 AM permalink
deleted
DUHHIIIIIIIII HEARD THAT!
Mission146
Mission146
  • Threads: 142
  • Posts: 16832
Joined: May 15, 2012
October 30th, 2013 at 11:08:08 AM permalink
Sometimes you have to rock the baby and give it a bottle before you can successfully put it to bed.
https://wizardofvegas.com/forum/off-topic/gripes/11182-pet-peeves/120/#post815219
thecesspit
thecesspit
  • Threads: 53
  • Posts: 5936
Joined: Apr 19, 2010
October 30th, 2013 at 11:21:57 AM permalink
Quote: Ibeatyouraces

All this for the silly bac trenders?



The trail is more interesting than the destination.
"Then you can admire the real gambler, who has neither eaten, slept, thought nor lived, he has so smarted under the scourge of his martingale, so suffered on the rack of his desire for a coup at trente-et-quarante" - Honore de Balzac, 1829
Wizard
Administrator
Wizard
  • Threads: 1493
  • Posts: 26489
Joined: Oct 14, 2009
October 30th, 2013 at 11:25:16 AM permalink
Do the advanced programmers on the forum approve of this Fisher-Yates shuffle I just wrote. As you can see, after it pulls out the card in the rn-th place in the old deck it slides everything after it one position, to keep the old deck sequential and without gaps.



void fisher_yates(int deck[], int NumCards)
{
int i,j,NewDeck[416];
unsigned int rn;
for (i=0; i<NumCards; i++)
{
rn=genrand_int32()%(NumCards-i);
NewDeck[i]=deck[rn];
for (j=rn; j<NumCards-1; j++)
deck[j]=deck[j+1];
}
for (i=0; i<NumCards; i++)
deck[i]=NewDeck[i];
}
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
MathExtremist
MathExtremist
  • Threads: 88
  • Posts: 6526
Joined: Aug 31, 2010
October 30th, 2013 at 11:28:29 AM permalink
Quote: Wizard

Do the advanced programmers on the forum approve of this Fisher-Yates shuffle I just wrote:



void fisher_yates(int deck[], int NumCards)
{
int i,j,NewDeck[416];
unsigned int rn;
for (i=0; i<NumCards; i++)
{
rn=genrand_int32()%(NumCards-i);
NewDeck[i]=deck[rn];
for (j=rn; j<NumCards-1; j++)
deck[j]=deck[j+1];
}
for (i=0; i<NumCards; i++)
deck[i]=NewDeck[i];
}


It's slightly biased.

Quiz: why?
"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
thecesspit
thecesspit
  • Threads: 53
  • Posts: 5936
Joined: Apr 19, 2010
October 30th, 2013 at 11:32:26 AM permalink
Quote: TheCesspit

The trick is to avoid the newbie mistake and end up with a cyclic shuffle (Sattolo's algorithm).


No possibility of selecting the current card to replace itself.


I think....
"Then you can admire the real gambler, who has neither eaten, slept, thought nor lived, he has so smarted under the scourge of his martingale, so suffered on the rack of his desire for a coup at trente-et-quarante" - Honore de Balzac, 1829
Wizard
Administrator
Wizard
  • Threads: 1493
  • Posts: 26489
Joined: Oct 14, 2009
October 30th, 2013 at 11:38:31 AM permalink
I disagree. If the first random number is 0, the first card in the shuffled deck will be the first card from the original deck.


However, I don't have my own answer, yet.

Quote: Ibeatyouraces

All this for the silly bac trenders?



That is why I asked for the donation to charity, which it looks like I'm not going to get.
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
wudged
wudged
  • Threads: 2
  • Posts: 998
Joined: Aug 7, 2013
October 30th, 2013 at 11:48:10 AM permalink
Quote: Wizard

Do the advanced programmers on the forum approve of this Fisher-Yates shuffle I just wrote. As you can see, after it pulls out the card in the rn-th place in the old deck it slides everything after it one position, to keep the old deck sequential and without gaps.



void fisher_yates(int deck[], int NumCards)
{
int i,j,NewDeck[416];
unsigned int rn;
for (i=0; i<NumCards; i++)
{
rn=genrand_int32()%(NumCards-i);
NewDeck[i]=deck[rn];
for (j=rn; j<NumCards-1; j++)
deck[j]=deck[j+1];
}
for (i=0; i<NumCards; i++)
deck[i]=NewDeck[i];
}



It's been a while since I've used C++, but can't you initialize the NewDeck array using NumCards instead of 416? Algorithm looks correct with the exception of the bias ME pointed out.

Check out the modulo bias that jml24 pointed out
thecesspit
thecesspit
  • Threads: 53
  • Posts: 5936
Joined: Apr 19, 2010
October 30th, 2013 at 11:49:13 AM permalink
Quote: Wizard

I disagree. If the first random number is 0, the first card in the shuffled deck will be the first card from the original deck.



Apologies, I agree with your disagreement now, misread the code.

I'm not sure you need the shuffle down though, as you can do the whole think in place, and that avoids copying the shuffled deck back into the new Deck as well. But there might be reasons to keep both old and new.

Swap Position [0] with a random card from [0-n]. The swap Position [1] with a random card from 1-n, and repeat.
"Then you can admire the real gambler, who has neither eaten, slept, thought nor lived, he has so smarted under the scourge of his martingale, so suffered on the rack of his desire for a coup at trente-et-quarante" - Honore de Balzac, 1829
JB
Administrator
JB
  • Threads: 334
  • Posts: 2089
Joined: Oct 14, 2009
October 30th, 2013 at 11:53:50 AM permalink
I see a few things wrong with this Fisher-Yates implementation:

1) The for() loop has one iteration too many; it is better to count down from (NumCards - 1) to 1

2) The process of obtaining a random number may result in a bias because 232 % NumCards may not necessarily be zero

3) There is no need for an extra array to hold the shuffled cards; they are shuffled in place with Fisher-Yates

The following code should implement it correctly:


void fisher_yates(int deck[], int NumCards)
{
int i, r, t;

for (i = NumCards - 1; i > 0; i--)
{
do
{
r = genrand_int32();
}
while ((r < 0) || (r >= i));

t = deck[r];
deck[r] = deck[i];
deck[i] = t;
}
}

The do() loop obtains random integers repeatedly until an integer in the needed range is found. Then the card in that random position is swapped with the card in position i.
Wizard
Administrator
Wizard
  • Threads: 1493
  • Posts: 26489
Joined: Oct 14, 2009
October 30th, 2013 at 12:06:35 PM permalink
Quote: wudged

It's been a while since I've used C++, but can't you initialize the NewDeck array using NumCards instead of 416?



Visual Studio doesn't let you. I tried. Seems that it should. I just know I'm going to get an array error when I use this code later for a bigger array.

Thanks JB for your comments. The only thing I have a hard time with is waiting for the random number to be in the desired range. That could take a while, as they are drawn from 0 to 2^32-1. I know computer are fast these days, but I can remember the punch card days.

Given this large cycle, I think taking the mod of a number as big as 4,294,967,295, would result in a very negligible bias. Perhaps that is the answer to ME's question.

Here is my new and improved code, per your other suggestions.


void fisher_yates(int deck[], int NumCards)
{
int i,j,random_card;
unsigned int rn;
for (i=NumCards-1; i>=0; i--)
{
rn=genrand_int32()%(i+1);
random_card=deck[rn];
for (j=rn; j<i-1; j++)
deck[j]=deck[j+1];
deck[i]=random_card;
}
}
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
AcesAndEights
AcesAndEights
  • Threads: 67
  • Posts: 4300
Joined: Jan 5, 2012
October 30th, 2013 at 12:20:18 PM permalink
Quote: Wizard

Visual Studio doesn't let you. I tried. Seems that it should. I just know I'm going to get an array error when I use this code later for a bigger array.


To define a variable-sized array you have to use a pointer and the new keyword. With a constant size array the compiler allocates that much space statically on the stack, but for a variable sized array you have to allocate memory on the heap. In C you would use malloc() which is what I was going to suggest, but I think in C++ you just use new.

reference.
"So drink gamble eat f***, because one day you will be dust." -ontariodealer
wudged
wudged
  • Threads: 2
  • Posts: 998
Joined: Aug 7, 2013
October 30th, 2013 at 12:28:31 PM permalink
What about:


void fisher_yates(int deck[], int NumCards)
{
int i,j,random_card, max;
unsigned int rn;

for (i=NumCards-1; i>0; i--)
{
max = floor(4294967295 / (i + 1)) * (i + 1);
do
{
rn = genrand_int32();
}
while ((rn < 0) || (rn >= max));

rn = rn % (i + 1);

random_card=deck[rn];
deck[rn] = deck;
deck = random_card;
}
}


This will strip off only the bias part and not spend "hours" trying to find a valid random number that avoids the bias.

(Edited to change the modulo to integer division when assigning the value to max)
JB
Administrator
JB
  • Threads: 334
  • Posts: 2089
Joined: Oct 14, 2009
October 30th, 2013 at 12:33:50 PM permalink
Quote: Wizard

Thanks JB for your comments. The only thing I have a hard time with is waiting for the random number to be in the desired range. That could take a while, as they are drawn from 0 to 2^32-1. I know computer are fast these days, but I can remember the punch card days.


I also have a problem with it...

Quote: Wizard

Given this large cycle, I think taking the mod of a number as big as 4,294,967,295, would result in a very negligible bias. Perhaps that is the answer to ME's question.


A better solution would be to generate only one random number per iteration; cast it to a double; divide it by 4294967296; multiply it by i; then cast it back to an int and put it in random_card.

Quote: Wizard

Here is my new and improved code, per your other suggestions.


In the for loop initializer, the middle condition should be i > 0 (not i >= 0). Also, moving cards about the array is unnecessary and slows down the function. Just swap the array values located in positions i and random_card.

Here's a great article about shuffling algorithms which illustrates bias using a 3-card deck:

http://www.codinghorror.com/blog/2007/12/the-danger-of-naivete.html
MathExtremist
MathExtremist
  • Threads: 88
  • Posts: 6526
Joined: Aug 31, 2010
October 30th, 2013 at 1:04:20 PM permalink
Quote: wudged


max = floor(4294967295 % (i + 1)) * (i + 1);
do
{
rn = genrand_int32();
}
while ((rn < 0) || (rn >= max));


This will strip off only the bias part and not spend "hours" trying to find a valid random number that avoids the bias


Yes, except that needs to be the integer-division operation, not the modulo operation.
max = floor(MAX_INT / (i + 1)) * (i + 1);
also, for clarity I'd either change the variable name to "toobig" or subtract 1 in the above formula and change the test to rn > max (strictly greater than).

The slight bias in the original code comes from doing a modulo operation on a range that does not evenly divide by the scaling range. The same bias is easier to see (and much larger) if you consider trying to use a 3-bit RNG to generate numbers between 0 and 2 inclusive. You want p(0) = p(1) = p(2) = 1/3, but without trimming the excess off the RNG range you end up with:

0 % 3 = 0
1 % 3 = 1
2 % 3 = 2
3 % 3 = 0
4 % 3 = 1
5 % 3 = 2
6 % 3 = 0
7 % 3 = 1


p(0) = p(1) = 3/8, while p(2) = 2/8. Whoops. The solution is to discard any RNG outcomes at the upper end of the range, in this case discarding 6 or 7, so then each outcome occurs with p=2/6.
"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
wudged
wudged
  • Threads: 2
  • Posts: 998
Joined: Aug 7, 2013
October 30th, 2013 at 1:16:57 PM permalink
Ah! Yes, thank you, it should be integer division and not modulo.
Wizard
Administrator
Wizard
  • Threads: 1493
  • Posts: 26489
Joined: Oct 14, 2009
October 30th, 2013 at 1:31:37 PM permalink
Quote: JB

In the for loop initializer, the middle condition should be i > 0 (not i >= 0). Also, moving cards about the array is unnecessary and slows down the function. Just swap the array values located in positions i and random_card.



You're right, that does seem to work just as well, and I don't have to bother with the card slide. Thank you.

However, I think worrying about the bias over taking the mod of 232 is rather ridiculous for practical purposes. I'm not saying discussion about it isn't worthy, but I don't want to slow down the program over it.

I'd compare this to a video poker player memorizing an exception that increases the return by 0.000000001.

Anyway, here is version 4, I think, of my shuffle.


void fisher_yates(int deck[], int NumCards)
{
int i,hold;
unsigned int rn;
for (i=NumCards-1; i>0; i--)
{
rn=genrand_int32()%(i+1);
hold=deck[rn];
deck[rn]=deck[i];
deck[i]=hold;
}
}
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
MathExtremist
MathExtremist
  • Threads: 88
  • Posts: 6526
Joined: Aug 31, 2010
October 30th, 2013 at 1:43:49 PM permalink
Quote: Wizard

You're right, that does seem to work just as well, and I don't have to bother with the card slide. Thank you.

However, I think worrying about the bias over taking the mod of 232 is rather ridiculous for practical purposes. I'm not saying discussion about it isn't worthy, but I don't want to slow down the program over it.


The slowdown would be minimal since you'd only need to redraw once every 16.8M tries. And it's fine for your personal code, but consider:
Quote: GLI Standard #11, 3.3.10 Scaling Algorithms

a) If a random number with a range shorter than that provided by the RNG is required for some purpose within the gaming device, the method of re-scaling, (i.e., converting the number to the lower range), is to be designed in such a way that all numbers within the lower range are equally probable.
b) If a particular random number selected is outside the range of equal distribution of re-scaling values, it is permissible to discard that random number and select the next in sequence for the purpose of re-scaling.


subitem (b) is exactly what we're talking about.
"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
Wizard
Administrator
Wizard
  • Threads: 1493
  • Posts: 26489
Joined: Oct 14, 2009
October 30th, 2013 at 4:15:34 PM permalink
Quote: MathExtremist

The slowdown would be minimal since you'd only need to redraw once every 16.8M tries.



Yes, but you have to check if it is in the range EVERY time. When I do a simulation I try to get in as many billions of trials as I can, and this will just slow it down. I think the accuracy from extra trials is worth more than the microscopic bias in excess mod cycle.

Quote:

subitem (b) is exactly what we're talking about.



Nice point! However, I'm not drawing a random number that is going to determine a real money outcome of a bet, but a simulation of something with billions of trials. I will keep that in mind, however, if I ever do write some code that might make it into a real money game.
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
MathExtremist
MathExtremist
  • Threads: 88
  • Posts: 6526
Joined: Aug 31, 2010
October 30th, 2013 at 5:12:35 PM permalink
Quote: Wizard

However, I'm not drawing a random number that is going to determine a real money outcome of a bet, but a simulation of something with billions of trials


Okay, but if throughput is the goal then there are other ways of generating a few new decks without using a new random shuffle each time. You could, for example, alternate between a new shuffle and a deck-reversal -- just reverse the array and re-run the shoe analysis. You'd have half the RNG calls. And then you could do a single-pass riffle-shuffle, reversing one-half of the array before interleaving. Run with that shoe, and then reverse *that* and run a 4th time. That gets you 25% of the RNG calls over time, with just deterministic array manipulations in the interim.
"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
charliepatrick
charliepatrick
  • Threads: 39
  • Posts: 2946
Joined: Jun 17, 2011
October 30th, 2013 at 5:26:17 PM permalink
Just an idea assuming the cost to call the RNG is quite high compared to the other code.

Assume that to generate a new deck you need 51 calls to RNG which creates [1-52],[1-51]....[1-2] for the first deck. Eventually after a number of decks you would have created a really random series of numbers. A51..A02 B51..B02 etc. Now you could generate a fixed algorithm to use (say) A51 B50 C49 etc for your next shuffle and so reuse the numbers in some predetermined (or time based) but non-obvious way.
98Clubs
98Clubs
  • Threads: 52
  • Posts: 1728
Joined: Jun 3, 2010
October 30th, 2013 at 7:21:54 PM permalink
One of the early F-Y tricks was to limit the call to the number of remaining un-shuffled cards.
I hope I did this right, its been 38 years. Nonetheless, this has been done with string arrays (useful in single-deck), AND
can be parsed by name ("As" = Ace of Spades) to image name ("As"=As.gif). Modern laguages do that, not this old stuff... lol.

Example using 416 cards in old-fashioned IBM-BASIC

INTEGER: REM declared all variables are integers DIM is not called to make the array dynamic
RANDOM: REM Pick the initial seed
For n = 416 to 2 step -1: REM decrement the random list by 1 each next
i = RND(n): REM will pick RND's of smaller range each pass
c(417-n)=d(i): REM position the the RND card in sequence
d(i)=0: zeroes the pick
d(n)=d(1 to i-1)&d(i+1 to )&d(i): REM decrement the RND choices as not i, maintain length of 416, ineligible picks=0
next n
c(416)=d(1): REM place the remaining card as last card in deck c
REM to shuffle these cards in c just let c(x)=d(x) and repeat from RAMDOM statement

Interesting to note that RANDOM can be called previously to determine the number of shuffles (say 1 to 9) somewhat randomly.
This was editted.
Some people need to reimagine their thinking.
DRich
DRich
  • Threads: 86
  • Posts: 11709
Joined: Jul 6, 2012
October 30th, 2013 at 10:05:40 PM permalink
nevermind, this was already said.

BTW, why can't I see a way to delete a post?
At my age, a "Life In Prison" sentence is not much of a deterrent.
Wizard
Administrator
Wizard
  • Threads: 1493
  • Posts: 26489
Joined: Oct 14, 2009
October 31st, 2013 at 7:20:55 AM permalink
Quote: DRich

BTW, why can't I see a way to delete a post?



You need to be an admin to do that.
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
RaleighCraps
RaleighCraps
  • Threads: 79
  • Posts: 2501
Joined: Feb 20, 2010
October 31st, 2013 at 11:08:18 AM permalink
Quote: DRich

nevermind, this was already said.

BTW, why can't I see a way to delete a post?



Just EDIT it, and remove all of your text.......
Always borrow money from a pessimist; They don't expect to get paid back ! Be yourself and speak your thoughts. Those who matter won't mind, and those that mind, don't matter!
beachbumbabs
beachbumbabs
  • Threads: 100
  • Posts: 14265
Joined: May 21, 2013
November 1st, 2013 at 6:53:46 AM permalink
Quote: RaleighCraps

Just EDIT it, and remove all of your text.......



Speaking of editing, Raleigh, I really needed to see this part of your signoff this morning...thanks!

"Be yourself and speak your thoughts. Those who matter won't mind, and those that mind, don't matter!"
If the House lost every hand, they wouldn't deal the game.
paisiello
paisiello
  • Threads: 21
  • Posts: 546
Joined: Oct 30, 2011
November 12th, 2013 at 9:51:20 PM permalink
I was following this earlier and one thing I was curious about was the reason you need to create a pre-shuffled deck in lieu of just a random number between 1 and 52. I would guess that there is some significant time savings pre-shuffling instead of having to move one element of an array around each draw?
  • Jump to: