Results 1 to 10 of 10

Thread: random shuffle

  1. #1

    Thread Starter
    Lively Member Deucy's Avatar
    Join Date
    Mar 2003
    Location
    Cali
    Posts
    85

    random shuffle

    NOTE: this doesn't concern a special language, this is a general algorithm..



    imagine a media player.. it has a list of songs to play.. and the user has it on shuffle mode.. so, each time it plays a song, that song shouldn't have been played before.. and so on..

    for the sake of simplicity, let's denote the song by a number.. so let's say we have a list of numbers from 1 to 10.. the random generator will pick number 5, and we put that in a list, let's call it playedList.. so the next time the random generator picks up the number 3.. then, it picks number 5 again.. it checks to see if 5 existed before, so it checks playedList.. it was there? pick another number.. and so forth..

    so far so good..
    but.. this is a list of 10.. imagine at the 9th time, it gets a random number, and it has to check that number with playedList, which by now has 8 numbers.. it doesn't seem like much waste of time.. but what about a list of 100? 1000? a million?

    we all know this, which is good.. and widely used..
    so i was thinkin' to come up with some kind of a bit-mask checking.. let's say we have a bit-mask variable, call it bitMask.. everytime we generate a number that hasn't been used before, we OR it to bitMask..

    here's a simple case scenario:

    init
    bitMask = 0000

    first time
    number 4 = 0100
    0100 AND 0000 = 0000 != 0100
    which means didn't exist before..
    set bitMask = bitMask OR 0100 = 0100

    second time
    8 = 1000
    0100 AND 1000 = 0000 != 1000
    didn't exist.. execute it..
    set bitMask = bitMask OR 1000 = 1100

    SIDE-NOTE: now, in bitMask, both 4 and 8 existed before, altho it's represented by one number 1100; 12 in decimal..

    third time
    2 = 0010
    0010 AND 1100 = 0000 != 0010
    didn't exist.. execute it..
    set bitMask = bitMask OR 0010 = 1110

    forth time
    4 = 0100
    0100 AND 1110 = 0100 != 0100
    existed before.. don't execute..
    don't change bitMask..

    fifth time
    1.. simple enough..
    bitMask = 1111

    SIDE-NOTE: note that so far, bitMask 1111 denotes that the numbers 1, 2, 4, 8 existed before..

    sixth time
    assume it gets to be the number 15..
    15 = 1111..
    1111 AND 1111 = 1111 = 1111
    this means that 15 existed before, altho we know, for sure, that it didn't..



    so, it seemed to be working fine, until the sixth time took place.. do you think we could come up with a workaround?

    i appreciate you reading up to THIS POINT..
    thank you..
    Call me Deuce.[vbcode]
    If User.name = "Deucy" And User.status = "online" Then
    Call Deuce()
    End If
    [/vbcode]

  2. #2
    pathfinder NotLKH's Avatar
    Join Date
    Apr 2001
    Posts
    2,397
    There are many ways to do what I believe you're suggesting.
    The fastest, easiest way, for me is something like the following:

    VB Code:
    1. Dim I_WAS_SELECTED() As Boolean
    2. Dim SelCount As Integer
    3. Dim MyUpper As Integer
    4.  
    5. Private Sub Command1_Click()
    6. Randomize
    7. Dim SELECTED_1_Thru_MyUpper  As Integer '
    8.     If SelCount = MyUpper  Then
    9.         Call REDIM_IT
    10.         MsgBox "Starting Over Again!"
    11.     End If
    12.    
    13.     SELECTED_1_Thru_MyUpper  = Int(Rnd * MyUpper )
    14.     While I_WAS_SELECTED(SELECTED_1_Thru_MyUpper ) = True
    15.         SELECTED_1_Thru_MyUpper  = (SELECTED_1_Thru_MyUpper  + 1) Mod MyUpper
    16.     Wend
    17.     SelCount = SelCount + 1
    18.     I_WAS_SELECTED(SELECTED_1_Thru_MyUpper ) = True
    19.     SELECTED_1_Thru_MyUpper  = SELECTED_1_Thru_MyUpper  + 1     '0 thru 14 equiv to 1 thru 15
    20.     MsgBox "Number " & SelCount & " Is " & (SELECTED_1_Thru_MyUpper )
    21.  
    22. End Sub
    23.  
    24. Private Sub Form_Load()
    25. MyUpper = 15
    26. Call REDIM_IT
    27. End Sub
    28. Private Sub REDIM_IT()
    29.         ReDim I_WAS_SELECTED(MyUpper - 1)
    30.         SelCount = 0
    31. End Sub

    As you can see, an array dimmed as boolean keeps track of whats been chosen. When a random selection is made, if that has been chosen already, the number is incrimented modularly, until an unchosen number is found.

    Also, if all the numbers have been chosen, then you reset the arrays and the counter, and start all over again.

    However, this is NOT the recommended way to do it, since it still is a time consuming process.

    I believe Guv, Jim Macnemara {sp?}, MartinLiss or Plenderj have a good "shuffling" system somewhere around here.

    And, I have a way useing copymem to remove elements from an array, without leaving holes, and then reinserting them back in the original order, which is definitely applicable to this problem.

    -Lou

  3. #3

    Thread Starter
    Lively Member Deucy's Avatar
    Join Date
    Mar 2003
    Location
    Cali
    Posts
    85
    yea, actually this is a good way and similar to what i was lookin' for..

    i'm not doin' a project or anything, i'm just concerned (and i always am) about finding an algorithm for this problem that is LEAST time-consuming..

    altho i'm not sure what could be less time-consuming, i'd like to know

    i'll check those guys u just mentioned..
    Call me Deuce.[vbcode]
    If User.name = "Deucy" And User.status = "online" Then
    Call Deuce()
    End If
    [/vbcode]

  4. #4
    Fanatic Member sql_lall's Avatar
    Join Date
    Jul 2002
    Location
    Up Above (i.e. AUS)
    Posts
    571

    Talking Best way.

    The best way i have come up with is this: (pseudo-code)
    Start with an array, play_order[1 to MAX_SONGS] = 1,2,3,4
    now loop through, swap each one randomly
    finish, play play_order[1], play_order[2]...

    E.g:

    VB Code:
    1. 'Assuming you have 10 songs.
    2. 'If you have 7, change each '10' to '7' etc...
    3. Dim play_order[10] as integer
    4. Dim a, temp as integer
    5. Dim rand_int as integer
    6. 'Put in sub somewhere...
    7. for a = 1 to 10
    8.  play_order[a]=a 'initialise
    9. next a
    10. for a = 1 to 10
    11.  rand_int=int(rnd*10)+1
    12.  temp=play[rand_int]
    13.  play[rand_int]=play[a]
    14.  play[a]=temp
    15. next a
    16. for a = 1 to 10
    17.  'Call you 'play the song' function here
    18. next a

    Apologies for any errors, i've been using C++, so am a bit out of touch with VB.
    Now, the running time of this is O(n), the same as the hypothesised bit-wise thing. If you have to check if each one before had been used, then this turns into O(n2). I think using the boolean array still results in an O(n2) on average, O(n) for the best case.
    BTW, you use something similar tho this when writing something to shuffle cards.
    sql_lall

  5. #5
    pathfinder NotLKH's Avatar
    Join Date
    Apr 2001
    Posts
    2,397
    This is the code that shuffles 15 numbers useing ShiftMem api.
    Basically, it extracts the number you just randomely selected from the stack of remaining possible choices, and reduces the range of choices in an ever decreasing process.

    VB Code:
    1. Option Explicit
    2.  
    3. Dim MyNumbers() As Integer
    4. Dim MyNumberSet() As Integer
    5. Dim MyChosen() As Integer
    6. Dim MyUpper As Integer
    7. Dim MyTop As Integer
    8.  
    9. Private Declare Sub ShiftMemory Lib "kernel32" Alias "RtlMoveMemory" (hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long)
    10. Private Sub Form_Load()
    11.     MyUpper = 15
    12.     Call SETUP_SOURCE
    13.     Call REDIM_IT
    14. End Sub
    15. Private Sub SETUP_SOURCE()
    16.         Dim MyI As Integer
    17.         ReDim MyNumberSet(MyUpper - 1)
    18.         For MyI = 0 To UBound(MyNumberSet)
    19.             MyNumberSet(MyI) = (MyI + 1)
    20.         Next MyI
    21.         ReDim MyNumbers(MyUpper - 1)
    22.         ReDim MyChosen(MyUpper - 1)
    23. End Sub
    24.  
    25. Private Sub Command1_Click()
    26.     Dim SELECTED_1_Thru_MyUpper  As Integer '
    27.     Dim MyI As Integer
    28.     Randomize
    29.     Call REDIM_IT
    30.         'List1.Clear
    31.     MyTop = MyUpper - 1
    32.     For MyI = 0 To UBound(MyNumbers)
    33.         SELECTED_1_Thru_MyUpper = Int(Rnd * (MyTop + 1))
    34.         MyChosen(MyI) = REMOVE_ELEMENTS(MyNumbers, SELECTED_1_Thru_MyUpper, MyTop)
    35.     Next MyI
    36.     'For MyI = 0 To UBound(MyChosen)
    37.     '    List1.AddItem MyChosen(MyI)
    38.     'Next MyI
    39. End Sub
    40.  
    41. Private Sub REDIM_IT()
    42.         ShiftMemory MyNumbers(0), MyNumberSet(0), 2 * MyUpper
    43. End Sub
    44. Private Function REMOVE_ELEMENTS(ByRef inArr() As Integer, ByRef RemoveWhichOne As Integer, ByRef HowManyToChooseFrom As Integer) As Integer
    45.     REMOVE_ELEMENTS = inArr(RemoveWhichOne)
    46.     If RemoveWhichOne <> HowManyToChooseFrom Then
    47.         ShiftMemory inArr(RemoveWhichOne), inArr(RemoveWhichOne + 1), 2 * (HowManyToChooseFrom - RemoveWhichOne)
    48.     End If
    49.     HowManyToChooseFrom = HowManyToChooseFrom - 1
    50. End Function

    The result is stored in the MyChosen array.


    -Lou

  6. #6
    New Member
    Join Date
    May 2003
    Location
    In a house. A medium-sized house. In the suburban area...
    Posts
    8
    I would suggest using sql_lall's method of swapping two over again and again, but as a general rule you should do this 5x times, where the amount of numbers in the list is x.

    So for a list of 10, swap them 50 times. It's simple, and it works.
    IF you.thief = TRUE THEN
    you.future.place = jail
    END IF

  7. #7
    Retired G&G Mod NoteMe's Avatar
    Join Date
    Oct 2002
    Location
    @ Opera Software
    Posts
    10,190
    I havn't read all the post over here, but what about if you have an array of all the songs, and an other array with the songs that have been played. (Use objects, then you only have to change the pointers, because the objects are not in the arrays, just the pointers).

    - Every time one is played, place it in the other array.
    - Move the songs that is in the play array forward, so you have no holes in the play array (keep track of how many it is in the play array)
    - Now pick one of the indexes in the array of the play list, and you will know for sure it has not been played....


    for 100 songs this will not take much time. And if you are using objects with pointers in the array, you will not use musch time sorting them out if there is 1000 of them either.....just a tought....

  8. #8

    Thread Starter
    Lively Member Deucy's Avatar
    Join Date
    Mar 2003
    Location
    Cali
    Posts
    85
    hold up hold up.. u mixed up two concepts u stirred Linked Lists and Arrays together ..

    you said have an array of songs, and each time a song is played, you remove it off the list, and shift up the rest.. now in the worst-case scenario, indeed you were reffered to the song with the index, but might end up shifting the whole list up.. which takes O(n) if they were n songs (right?)..

    then you backed up and said if you used pointers, then you won't have much time shifting them up, so you're suggesting Linked Lists (conceptually).. but hold on a second.. back to square one.. you lost the referrence to a song with the index, now you have to traverse through the list to find the song, which again, takes O(n) with n songs

    this is fine if it's a small list of songs.. but i'm not actually making a media player, i was just discussing the algorithm of randomizing a list (e.g. a Media Player )

    so far, persaonlly, i find the algorithm in which you fill an array with the numbers ordered and randomly interchange their positions in the list then just traverse through the list iteratively the most-effective easily-implemented algorithm..
    Call me Deuce.[vbcode]
    If User.name = "Deucy" And User.status = "online" Then
    Call Deuce()
    End If
    [/vbcode]

  9. #9
    Addicted Member
    Join Date
    Aug 2002
    Location
    London UK
    Posts
    255
    A neat way to shuffle stuff like audio tracks or cards is to stick them in an array and swap random numbers. For example let's say we're using 52 cards. The procedure you'd want to follow would be:

    1: Pick 2 random numbers between 1 and 52, call them x and y.
    2: Stick card x in a temporary variable.
    3: Stick card y where x was.
    4: Stick the card from the temporary variable, where y was.
    5: Repeat.

    For that list of 52 cards I found that 500ms worth of shuffling was plenty. I'm not sure how well this would work for large lists though.
    Not at all related to sheep...

  10. #10
    I don't do your homework! opus's Avatar
    Join Date
    Jun 2000
    Location
    Good Old Europe
    Posts
    3,863
    You don't have to use that shuffeling thing!
    Go for the random pick, but use a second array from which you pick the songs.

    PseudoCode:
    You have the Songlist (1..10) and the PickList (1..10), the later holds the numbers of Songs from the songlist ( at the beginning that would be easy Picklist(1)=1 Picklist(2)=2 ....) Store the Shuffle in a Playlist(1..10)
    First Pick
    Pick one INDEX of the PickArray(1..10) and store its Value in Playlist(1). (For example You picked randomly Index 4, Since Picklist(4)=4 You have Playlist(1)=4)
    Now remove the picked Index from the Picklist, move all indice which are greater one step down and resize the Picklist.
    Note: The Picklist loks like that now Picklist(1)=1, Picklist(2)=2, Picklist(3)=3, Picklist(4)=5,....Picklist(9)=10
    Second Pick
    Pick one INDEX of the PickArray(1..9) and store its Value in Playlist(2). (For example You picked randomly Index 4, Since Picklist(4)=5 You have Playlist(2)=5)
    Now remove the picked Index from the Picklist, move all indice which are greater one step down and resize the Picklist.
    Note: The Picklist loks like that now Picklist(1)=1, Picklist(2)=2, Picklist(3)=3, Picklist(4)=6,....Picklist(8)=10
    ...................
    That way you don'T have to draw countless times to find a valid pick. The timeconsuming part would be the rearranging of the picklist after each draw.
    You're welcome to rate this post!
    If your problem is solved, please use the Mark thread as resolved button


    Wait, I'm too old to hurry!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width