Unrepeated random numbers

Miscellaneous Forums/General Discussion/Unrepeated random numbers

Hi.
I'm trying to create a card game for my neice.
My problem is, how do I generate 52 random numbers without any of them being repeated?.
If I use a For - Next loop such as:-

For I=1 to 52
Number(I)=Rand(52)
Next

I get 52 numbers with many of them the same.
Please help if you can.

Fert.

You don't want to generate 52 random numbers, you want to shuffle an array of 52 numbers, slightly different.

as Pert said... but in much more easy to follow sense.

you want to make a list of the 52 cards.

You then shuffle them.... My favorite way is to really shuffle them. 2 ways.

First, take the bottom card and move it to slot 2, then take the bottom card and move to slot 4 then 6 8 10 12 14 16 18...

do that a random number of times.

move the bottom 26 cards to the top. after each shuffle, so you don't have the top card always in the same place.

then do every 3, and then every 7... each for random numbers of times. Then do at least 1 more like the first one to make extra sure it is mixxed up.

Then, deal like the real game, restack the cards as you play so the user's choices effect the random nature of the cards. This will create a truely random deck. As truely random as a real deck of cards is.

This can be used for any kind of card game, even with many decks and with many numbers of cards.


EDIT.. I forgot to say the other way.... generate a random number from 1 to 52 (or however many card you have) thjen take all the card below that number and move them to the top, repeat 1000 times.... this isn't as realistic.. but makes for smaller code.

This is one way to do it if you are using arrays:
Strict

Local deck:Int[52] ' This is our deck of cards.

SeedRnd MilliSecs() ' Make rand() a bit more random

' Fill deck with cards
For Local i:Int = 0 Until 52
	deck[i] = i
Next 

' Shuffle the deck
For Local i:Int = 0 Until 1000
	Local c1:Int = Rand(0,51)
	Local c2:Int = Rand(0,51)
	Local t = deck[c1]
	deck[c1] = deck[c2]
	deck[c2] = t
Next


Graphics 800,600,0,2
SetBuffer BackBuffer()

SeedRnd MilliSecs()

Dim cards_array(52)
For i=1 To 52
	cards_array(i)=i
Next

For i=0 To 2000
	shuffle(Rand(1,52),Rand(1,52))
Next

textX=0 : textY=0

For i=1 To 52
Text textX*50,textY*20, cards_array(i)
textY=textY+1
If textY>12 : textY=0 : textX=textX+1 : EndIf
Flip
Delay 500
Next


WaitKey


Function shuffle(cardA,cardB)
	cards_array(0)=cards_array(cardA)
	cards_array(cardA)=cards_array(cardB)
	cards_array(cardB)=cards_array(0)
End Function


EDIT: Man, you guys so beat me to it.

I tried to make mine do a more realistic shuffle (by doing it the same way a person would).


in bmax:
Global cardsArray:Int[52]

SeedRnd(MilliSecs())'seed the random number generator to help prevent pattern repeating

ResetCards()
Shuffle()

For Local i:Int = EachIn cardsArray
	Print i
Next



Function Shuffle(res:Int = 20)
	Local split1:Int
	Local Split2:Int
	Local Split3:Int
	Local Cut:Int[]
	
	For Local i:Int = 0 To res
		Split1 = Rand(20, 30) 'remove approximately half the pack from the bottom
			Cut = CardsArray[Split1..]
			CardsArray = CardsArray[..Split1]
			
		Split2 = Rand(0, Len(Cut)/2) 'Drop part of that from the top onto the top of the main pack
			CardsArray = ConcatIntArray(Cut[..Split2], CardsArray)
			Cut = Cut[Split2..]
			
		Split3 = Rand(0, Len(Cut)/2) 'drop the next part on top of that
			CardsArray = ConcatIntArray(Cut[..Split3], CardsArray)
			Cut = Cut[Split3..]
		'drop the rest
		CardsArray = ConcatIntArray(Cut, CardsArray)
	Next
End Function


Function ResetCards()
	For Local i:Int = 1 To 52
		cardsArray[i-1] = i
	Next
End Function


Function ConcatIntArray:Int[](Array1:Int[], Array2:Int[])
	Local result:Int[]
	
	For Local count:Int = 0 To Len(Array1)-1
		result = result[..Len(result)+1]
		result[count] = Array1[count]
		
	Next
	
	For Local count2:Int = 0 To Len(Array2)-1
		result = result[..Len(result)+1]
		result[count+count2] = Array2[count2]
			
	Next
	
	Return result
End Function


Yes, my "shuffle" function would be more appropriately named "switch".

There must quite litterally be hundreds of ways to shuffle a deck of cards. Here's my version:
SuperStrict

Type CardDeck
	Field myCards:Byte[52]
	
	Method New()
		For Local i:Int = 0 Until myCards.length
			myCards[i] = i
		Next
	EndMethod
	
	Method shuffle()
		Local n:Byte = 51
		While n > 0
			Local k:Byte = Rand(0,n)
			Local temp:Byte = myCards[n]
			myCards[n] = myCards[k]
			myCards[k] = temp
			n:-1
		EndWhile
	EndMethod

	Function getCard:String( cardNumber:Int )
		Local temp:String
		Select cardNumber / 13
			Case 0
				temp = "Clubs"
			Case 1
				temp = "Diamonds"
			Case 2
				temp = "Hearts"
			Case 3
				temp = "Spades"
		EndSelect
		Return (cardNumber Mod 13) + " of " + temp
	EndFunction
EndType

Local myDeck:CardDeck = New CardDeck
myDeck.shuffle
For Local i:Int = EachIn myDeck.myCards
	Print CardDeck.getCard(i)
Next


I wrote this a while back. It sorts using lists, which apparently use bubble sorting as opposed to a quicksort on array.sort( ), so maybe it should be modified to use arrays.

Strict

Module GameStub.RandomSort

ModuleInfo "Version: 1.00"
ModuleInfo "Author: Michael Reitzenstein"
ModuleInfo "License: N/A"
ModuleInfo "Copyright: Michael Reitzenstein"
ModuleInfo "Modserver: N/A"

Import BRL.LinkedList
Import BRL.Random

Rem

	Randomly sorts arrays And lists
	
End rem


Type TRandomSort

	Function SortArray:Object[]( Array:Object[] )
		
		Return SortList( ListFromArray( Array ) ).ToArray( )
		
	End Function
	
	Function SortList:TList( List:TList )
	
		Local ElementList:TList = New TList
		Local ResultList:TList = New TList
		
		For Local obj:Object = EachIn List
		
			ElementList.AddLast( TRandomSortElement.Create( obj ) )
			
		Next
		
		ElementList.Sort( )
		
		For Local rse:TRandomSortElement = EachIn ElementList
			
			ResultList.AddLast( rse.Value )
			
		Next
		
		Return ResultList
	
	End Function
		
End Type

Type TRandomSortElement
	
	Field Sort = Rand( 1, 262144 )
	Field Value:Object
	
	Function Create:TRandomSortElement( Value:Object )
		
		Local rse:TRandomSortElement = New TRandomSortElement
		
		rse.Value = Value
		
		Return rse
		
	End Function
	
	Method Compare( obj:Object )
		
		Return Sort - TRandomSortElement( obj ).Sort
		
	End Method
	
End Type


fert: of course shuffling is a good idea, but to answer your original question you have to make an array 52 long, then make a random number and loop through the array to check if it is already in there, if not add it. Repeat this until the array is full, but it might be a bit slow at the end as the 52nd slot may take ages to come up with an unused number!

as Pert said... but in much more easy to follow sense
No offence intended, but that made me laugh out loud for real. |oD

Wow what a great response.
Thank you all for your help.
I will try each solution and use the one that suits me best. Being something of a novice, it may take me a little while to understand all the concepts you have put forward. I will try my best.
Thanks again.
Fert

For what it's worth I use:

SeedRnd MilliSecs()

Dim CARD(51),CHECK(51)

Shuffle()

Function Shuffle()


For loop=0 To 51
CHECK(loop)=0
Next

aa=0

Repeat

rr=Rnd(51)
While CHECK(rr)=1
rr=Rnd(51)
Wend

CHECK(rr)=1
CARD(rr)=aa
aa=aa+1

Until aa=52

End Function

You can easily check that it's selecting different cards by using:

For ll=0 To 51
Print CARD(ll)
Next

CHECK simply checks to see if that card has already been used.


Regards

Thanks again for all your help.
I must say a special thank you to BigH. His offered solution is exactly what I need.
Merry Christmas to all.

Regards,
fert

His offered solution is exactly what I need.
Except it has a best-case quadratic time complexity. Might not be a big deal when you're only dealing with 51 elements tho'.

This one is probably the least efficient of the bunch, but an elegant solution if I may say so myself:
include "vectorC.bb" ; <a href="http://blitzbasic.com/Community/posts.php?topic=53352" target="_blank">http://blitzbasic.com/Community/posts.php?topic=53352</a>
seedrnd millisecs()

function shuffle(deck.vectorC)
	total = vector_count(deck)
	for i = 0 to total-1
		card = vector_remove_element(deck, rand(0, total-i-1))
		vector_push(deck, card)
	next
end function


deck.vectorC = vector_new()
for i = 0 to 52-1
	vector_push(deck, i)
next

shuffle(deck)

for i = 0 to 52-1
	print vector_get(deck, i)
next


P.S. actually, I think I like FlameDuck's better.

What exactly is "a best-case quadratic time complexity" please? If you could give me the answer in easy to understand English I'd appreciate it!!
Regards.

Read up on complexity theory on Wikipedia: http://en.wikipedia.org/wiki/Big_O_notation

EDIT: the "External Link" on that Wikipedia page is probably more relevant and easier to follow: http://www.cprogramming.com/tutorial/computersciencetheory/algorithmicefficiency1.html

In simple terms, it could take 1 try to find a unique number for that final slot or it could take several seconds or even hours. Who knows?

What exactly is "a best-case quadratic time complexity" please?
Time complexity is a measure of how much time an algorithm takes to execute for n number of elements. When something is "best-case" it reflects the quickest possible outcome of an algorithm (in the above mentioned example this would be a case where two random numbers are never the same).

Now the best possible time complexity is constant - where an algorithm takes the same time, regardless of how many elements it has. Inserting values into the end or begining of a linked list is a good example of an operation with constant time complexity.

The second best is logarithmic time complexity - where an algorithm takes at most log(n) time to complete. An example of this is the binary search, or "numbers guessing game", where you start in the middle of the range, and then halve if it's lower, or add half if it's higher each time, eventually narrowing it down without having to check all elements.

Way down on the list is quadratic time complexity, where the time taken is n^2. An example of such an algorithm is the bubblesort algorithm. You can usually identify such algorithms by nested 'For' loops. This means that bubblesorting 100 elements takes 10000 time. Bubblesorting 200 elements takes 40000 time, and so on, each time you double your elements, you quadruple the time. As you can probably tell, such routines can quickly grind to a halt when using large data sets.

Now for small values of n (like 52) this discussion is mostly academic, particularly if Rand delivers mostly evenly distributed random numbers, you won't have many misses, although a gausian distributed random function could potentially run "forever".

P.S. actually, I think I like FlameDuck's better.
The most immediate problem with mine is that cards at the front of the array could have a tendancy to stay at the front of the array. Whether this is actually a problem isn't something I've tested to any larger degree, and probably isn't that different from a real world deck of cards.

Thanks for that info. As I write mainly puzzle games I've used that bit of code a lot and never (up until now) had that problem - but then I'm never using large amounts of data either.
Regards

You can also just keep the list and run the list through another list of numbers you created... in other words
1,2,3,4,5,6,7,8,9,10
gets reordered to
2,6,3,9,8,10,1,4,7,5

then, lets say you draw 3 cards... just to be simple.
2,6,3 and then plop them at the bottom of the deck

9,8,10,1,4,7,5,2,6,3

then reorder it with the same list

8,7,10,6,2,3,9,1,5,4

and draw some more cards

8,7,10,6

2,3,9,1,5,4,8,7,10,6

then reorder the list with that same key list.

6,4,9,10,7,6,2,1,8,5


This doesn't use a random number generator at all and generates random numbers. Because it uses the user to seed it's own random number generation system with built in unique numbers.

When you are playing say... poker.. you draw off the 5 starting cards, but then also draw off the cards that the players discard, and it reorders the list as you put the discarded cards at the decks bottom first.



This can be used for millions of unique numbers and only take the time it takes to process 1 unit times that million... so... it is just n times... rather than n^2 times.... is that an acceptable reduction?

Here's another simple one, also using lists.

SuperStrict

Type card
	Field shuffleorder:Int
	Field id:Int

	Method Compare:Int(otherObject:Object)
		Local c:card= card(otherObject)
		If c = Self Return False
	
		If shuffleorder= c.shuffleorder
			Return True
		Else
			Return Sgn(shuffleorder - c.shuffleorder)
		EndIf
	End Method	

End Type


Function List(deck:tlist)

	For Local c:card = EachIn deck
		Print c.id + " - ( " + c.shuffleorder + " )"
	Next	

End Function



Function shuffle(deck:tlist)

	Local randSize:Int = deck.count() * 2

	For Local c:card = EachIn deck
		c.shuffleorder = Rand(randSize)
	Next
		
	deck.sort()

End Function




'--Test Program
' make a deck of cards:

Local c:card
Local deck:tlist = New tlist

For Local n:Int = 1 To 52

	c = New card
	c.id = n
	deck.addLast(c)
Next


Print "Before-----------"
List deck

shuffle deck

Print "After-----------"
List deck


This is a revised version of my code which is slightly shorter and prevents any possibility of locking up the computer.

SeedRnd MilliSecs()

Dim CARD(52),CHECK(52)

Shuffle()


Function Shuffle()

For loop=0 To 51
CHECK(loop)=0
Next

For loop=0 To 51

rr=Rnd(51)

While CHECK(rr)=1
rr=rr+1
If rr=52 Then rr=0
Wend

CHECK(rr)=1
CARD(rr)=loop

Next

End Function

Regards.

This is the "random permutation" problem. As far as I know, the best way to do it is to iterate through every "card" in the "deck", and swap it with a random card between that card and the end of the deck (NOT every card in the whole deck).

For example (where Deck is an array of TCard objects):
For Local i:Int = 0 to Len(Deck)-1
  Local card:TCard = Deck[i]
  Local swapIndex:Int = Rand(i, Len(Deck)-1)
  Deck[i] = Deck[swapIndex]
  Deck[swapIndex] = card
Next


dim Cards(52) '0=card not yet picked, 1=card been 
dim CardsPicked(52) 'array index 1=first card of shuffled deck, array index 52=last card of shuffled deck, array value at index = id number/index of card (ie 9 of hearts) 
picked
;create shuffled deck
For i=1 to 52
Card=rand(1,52)
while Cards(Card)=1 
Card=Card+1
if Card>52 then Card=1
wend
Cards(Card)=1
CardsPicked(i)=Card
next



In that case

Type card
	Field id:Int
End Type

Function Shuffle:tlist(deck:tlist)
	Local ShuffledDeck:tlist = New tlist
	Local o:Object
	
	While deck.count()
		o = deck.ValueAtIndex(Rand(deck.count()-1))
		deck.remove(o)
		ShuffledDeck.AddLast(o)
	Wend

	Return ShuffledDeck
End Function




'--Test Program
' make a deck of cards:

Local c:card
Local deck:tlist = New tlist

For Local n:Int = 1 To 52

	c = New card
	c.id = n
	deck.addLast(c)
Next


'shuffle And list

deck = Shuffle(deck)
list deck

Function List(deck:tlist)

	For Local c:card = EachIn deck
		Print c.id 
	Next	

End Function

[EDIT] changed Shuffle() to use type :object, so that it willl shuffle a list full of anything, not just cards.

As far as I know, the best way to do it is to iterate through every "card" in the "deck", and swap it with a random card between that card and the end of the deck (NOT every card in the whole deck).
*cough* Really? :o>

Actually, his method is a bit different if you read it carefully, in that he picks a card from Rand(n,52), but you pick from Rand(0,52).

I still stand behind the solution from Peter and I... but that depends on whether you consider human like 'bad' shuffling to be a positive or negative!

As the world and his wife is getting in on the act, here's one from the archives (circa 2001)...

Dim deck(51)

SeedRnd MilliSecs()

Graphics 640, 480

setup_deck()

Repeat
    Cls
    shuffle_deck()
    tx = 100
    nc = 0
    p = 0
    
    Text 0, 100, "Card No"
    Text 0, 150, "Value"
    Text 0, 200, "Card"
    Text 0, 250, "Total"
    
    Repeat
        p = p + card_value(deck(nc))
        If (p = 21) And (nc = 1) Then Text 320, 10, "P O N T O O N !", 1, 0
        If (nc = 4) And (p <= 21) Then Text 320, 10, "F I V E  C A R D  T R I C K !", 1, 0
        If p > 21 Then Text 320, 10, "B U S T !", 1, 0
        
        Text tx, 100, deck(nc)
        Text tx, 150, card_value(deck(nc))
        Text tx, 200, show_deck$(deck(nc))
        Text tx, 250, p
        
        nc = nc + 1
        tx = tx + 40
    Until (p => 21) Or (nc = 5) Or (WaitKey() = 27)
Until KeyDown(1)

End

;

Function shuffle_deck()
    For s=0 To 50
        r = Rand(s + 1, 51)
        tmp = deck(s)
        deck(s) = deck(r)
        deck(r) = tmp
    Next
End Function

Function setup_deck()
    For c=0 To 51
        deck(c) = c + 1
    Next
End Function

Function show_deck$(cardval)
    suit$ = "SHCD"
    cardstr$ = ""
        c = cardval Mod 13
        s = Floor((cardval - 1) / 13)
        Select c
            Case 0
                cardstr$ = "A"
            Case 10
                cardstr$ = "J"
            Case 11
                cardstr$ = "Q"
            Case 12
                cardstr$ = "K"
            Default
                cardstr$ = Str$(c + 1)
        End Select
        cardstr$ = cardstr$ + Mid$(suit$, s + 1, 1)
        Return cardstr$
End Function

Function card_value(cardval)
    cv = (cardval Mod 13) + 1
    If cv > 10 Then cv = 10
    Return cv
End Function


Okay, so it uses the same method as denzilwhatisface to shuffle.

The first person to guess which card game I was writing wins a chocolate santa that's had his head bitten off...

um.... snap?

Ah, sorry...I just sold the santa, disguised as an XBox360, on ebay for 500 quid...

*cough* Really? :o>

Yeah, I'm sure I was told at some point that doing it that way was more uniformly random (though only slightly, probably).

Yeah, I'm sure I was told at some point that doing it that way was more uniformly random (though only slightly, probably).



I suppose the question is, do you want a uniformly random shuffling routine, or do you want a realistic one?

The chances of two cards that were grouped together before a real shuffle staying together after the shuffle are fairly high. I know of at least two card tricks that rely on this fact.

Oh. Right. Sorry, I was replying to the first post really, I only skimmed the rest of the thread and didn't really think about real card shuffling.

1Hmm, might be a good idea to have inbuilt uniformly-random shuffling routines for lists and arrays, anyway, though - I can think of other uses. What do people think?

Classic case of overengineering!

He didn't ask how to shuffle, he asked how to get 52 non-repeating random numbers!
(Which we can assume are the numbers 1 thru 52, making our task easier.)

Dim Deck(52)

SeedRnd MilliSecs()

For Loop = 1 to 52
   Repeat
      A = Rand(0, 51)
   Until Deck(A) = 0
   Deck(A) = Loop
Next


You will need to reset all elements of Deck() to 0 if you want to generate a new set of non-repeating numbers.

Hi again all.

In reply to sswift.

That's exactly right, I did ask for a random number generator, and I love your example, thank you.

I fully appreciate all the posts on this subject as I hope to eventually have the expertise to implement all or any of the solutions offered which best suit any future project I may develope.

Best wishes,
fert

Here's a more explanatory, non-Max version of mine:
; Set up the deck
Dim Deck(52)
For i = 1 To 52
  Deck(i) = i
Next


; Shuffle the deck
SeedRnd MilliSecs()
For i = 1 To 51 ; Not 52 since you could only swap the last card with itself!
  card = Deck(i) ; Select each card, in turn
  swapWith = Rand(i, 52) ; Choose a random card to swap it with between this card and the last one
  Deck(i) = Deck(swapWith) ; Swap!
  Deck(swapWith) = card    ;
Next


; Print the shuffled deck
For i = 1 To 52
  Print Deck(i)
Next


actually Sswift... your solution has a major flaw in it...

there are only like 1000 possible decks then. Because seed rand hits up a list of random numbers based on markers... spread out over the list of random numbers. So your deck will be the same if it happens to be generated at the same millisecond.. (If I understand how it is picking the number... that is, based on what thousandth of a second you are at... meaing only 1000 possible number starting points.. and only 1000 decks.)

Mine offeres a truely random number... oddly enough... because it doesn't use the computers random numbers.

actually Sswift... your solution has a major flaw in it...

there are only like 1000 possible decks then. Because seed rand hits up a list of random numbers based on markers... spread out over the list of random numbers. So your deck will be the same if it happens to be generated at the same millisecond.. (If I understand how it is picking the number... that is, based on what thousandth of a second you are at... meaing only 1000 possible number starting points.. and only 1000 decks.)



Damp, if I understand your one correctly (an arduous task), you don't have any random numbers, which results in the same sequence of cards each time. Unless you are implying that somehow the cards are initialized in a completely random way before calling your routine, in which case, what's the point in your routine?

SeedRnd Millisecs() is about the best way of achieving a reasonably random number in Blitz because the chances of it being called when millisecs() returns exactly the same number is so slim it's not worth worrying about.

Mine offeres a truely random number... oddly enough... because it doesn't use the computers random numbers.

Where does yours get the random numbers from then?

Where does yours get the random numbers from then?

The player. Which makes that argument inaccurate, because random numbers are usually generated from values that can expected to be random, such as:
-Mouse Position
-Window Position
-Available Memory
-Number of windows
-System time
etc.

In the end, random numbers can be pretty random... and it's all based on the user. It's not impossible to write your own generator, either.

I'm just being a pain, though. I don't think DampeS8N's idea is explained clearly enough. It sounds to me like you're getting the user to do a bit of the shuffling?


DampeS8N (Posted 2 hours ago)
actually Sswift... your solution has a major flaw in it...

there are only like 1000 possible decks then. Because seed rand hits up a list of random numbers based on markers... spread out over the list of random numbers. So your deck will be the same if it happens to be generated at the same millisecond.. (If I understand how it is picking the number... that is, based on what thousandth of a second you are at... meaing only 1000 possible number starting points.. and only 1000 decks.)




No offense, but are you on crack, or some other mind altering substance? :-)

Millisecs() returns a number between -2 billion and +2 billion. This number depends on how long your PC has been on.
It doesn't return a number between 1 and 1000 depending on where you are within a second or whatever you think it does. It just increments by 1 every millisecond.

So while yes, theoretically millisecs() could generate the same deck, so can shuffling the cards since there are only X number of possible combinations of 52 cards.
I think 4 billion different decks is sufficiently random. :-)

>of course shuffling is a good idea, but to answer your
>original question you have to make an array 52 long, then
>make a random number and loop through the array to check
>if it is already in there, if not add it. Repeat this
>until the array is full, but it might be a bit slow at the
>end as the 52nd slot may take ages to come up with an
>unused number!

Use 2 arrays of the same size and use a variable(source_total) to hold the number of cards in the 'source' array. Then simply move a random card(rnd(1,source_total) could be #25) from the 'source' array to the next sequential slot in the 'destination' array(dest_num, could be #1). Then delete the card from the 'source'(source_array(25)=0), move the cards following the moved card(source_array(26) to source_array(dest_num)), subtract one from source_total and move all following cards in array 1 up by 1 step(source_array(25)=source_array(26) etc).


Andy

Andy: Drop the arrays and use linked lists. Gets rid of having to step through a bunch of 0's. Brings things much closer to O(N) time :)

>Andy: Drop the arrays and use linked lists. Gets rid of >having to step through a bunch of 0's.

There is really no need for 0's in my proposal and you are only processing the actual number of elements in each array. The 0 in my above post is just to make the whole process more transparent and to visualise 'moving' the card from one array to the other.

> Brings things much >closer to O(N) time :)

As far as I recall, arrays are faster than types.

My proposal actually spends less time towards the last part of the shuffle than it does towards the first because The number of elments in destination_array increases and the number of elements in source_array decreases, thus you have less elements to move in source_array.


Andy

sswifts one is good and DampeS8N is crazy but hopefully now rehabilitaed.

Andy's solution sounds identical to mine in principle. I'm making an optimization: instead of two separate arrays, the last N elements of my array is the randomized deck. Essentially - select a random card from the first 52 cards and move it to the end of the deck, select a random card from the first 51 cards and move it to the end of the deck, etc. Of course, I'm also using my vector container library (which uses banks) instead of straight arrays to simplify things (for me, anyway.)

Using linked-list(s) means you'll have to walk the source list every time you want to find the Nth random element. Using array(s) means you'll have to move elements back to fill in your hole when you need to delete one. Both approaches have disadvantages for this application.

denzilquixode solution is the one, all you need to do is swap.

Interestingly enough it's also the solution used in a lot of games to decide what powers ups etc to deliver to the players. It allows for wieghting of random numbers, ie. some things get delivered more often than others.

Take the following sequence "1112344444", if we shuffle it and use the new sequence to determine what powerups we deliver to the player we can always guarantee that 50% of the powerups will be of Type 4, there will always be 3 of type 1 and so on...

Anyway enough rambling, I think I must still be drunk.

That's how I weight the power ups in Xmas Bonus, a list (array) of numbers and repeats are allowed, then a random number is picked that is the array index.

Dim Deck(52)

X = MilliSecs()
If X <= 0 Then X = -X + 1

For Loop = 0 To 51
   X = (2 * X) Mod 53	
   Deck(Loop) = X-1
Next


This algorithm will produce a unique sequence of non-repeating random numbers between 0 and 51 for any given seed. In theory. I ran it 10,000 times to make sure.

>Andy's solution sounds identical to mine in principle.

Sure is, but yours is cleverer :)


Andy

Hm... scratch that.

There's one limitation which I failed to take into account. With this algorithm, X cannot equal or be a multiple of 53. And I suspect that even if you make sure it is not a multiple of 53 that there will only actually be 52 possible decks.

...

Yup, I've just confirmed that. Deck 54 is the same as Deck 1. :-(

Anyway, as for the origin of the algorithm, I learned while reading about random numbers that it is possible to generate non-repeating squences of them. I then found this page:

http://www.brpreiss.com/books/opus4/html/page472.html

Which explains how to generate such a sequence of random numbers. I used the equation at the bottom of the page, which conveniently had a period of M-1 and was unable of producing a 0. This worked out well because 53 is a prime number, and if the period is M-1 because it is impossible to get a 0, then you end up with a period of 52 which is exactly the number of cards we need.

But what the page did not make clear that I have determined by experimentation is that whatever M is is the maximum number of different sets of random numbers you will be able to get.

So if you want a large number sets of of random numbers, the random numbers generated also must be large. And you can't normalize them to 0..1 as suggested at the bottom, because if you do that, then you would end up with some numbers repeating when you converted them back to an integer range.

So that sucks.

But hold on a second... I think I just thought of a way to improve the number of decks you can get.

All you need to do is generate a random number at the start, and then use that to rotate the deck by adding that value to the result, and then doing a mod 52. That would give you 52*52 ... 2704 different possible decks. And it would still be linear time.

This thread gives great insight into why games don't get finished. :)