PDA

View Full Version : Trying to produce random numbers that don't repeat



marcuslee
10-09-2008, 21:05
I'm trying to produce random numbers that don't repeat. The method I have read online uses GOTO, but since thinBasic doesn't have GOTO, I have been racking my brains to figure it out. I have two versions. The first version I know for a fact will never produce non-repeating random numbers 100% of the time, but it will occasionally. The second version I was hoping would do it everytime, but something is missing.

First Version:



DIM y, z AS LONG
DIM x( 10 ) AS LONG
DIM msg AS STRING

RANDOMIZE

ARRAY ASSIGN x( ) = 11

FOR y = 1 TO 10
x( y ) = INT( RND( 1, 10 ))
FOR z = 1 TO ( y - 1 )
DO UNTIL x( z ) <> x( y )
x( y ) = INT( RND( 1, 10 ))
LOOP
NEXT
NEXT

ARRAY SORT x( )

FOR y = 1 TO 10
msg = msg & $CR & x( y )
NEXT

MSGBOX 0, msg


Second Version:



DIM w, y, z AS LONG
DIM x( 10 ) AS LONG
DIM msg AS STRING

w = 1

RANDOMIZE

ARRAY ASSIGN x( ) = 11

FOR y = 1 TO 10
z = INT( RND( 1, 10 ))
DO UNTIL w = 0
w = ARRAY SCAN x( ), = z
IF w <> 0 THEN z = INT( RND( 1, 10 ))
LOOP
x( y ) = z
NEXT

ARRAY SORT x( )

FOR y = 1 TO 10
msg = msg & $CR & x( y )
NEXT

MSGBOX 0, msg



What am I missing?

Mark ??? :-[

Michael Hartlef
10-09-2008, 21:26
Hi Mark,

looks like you work on a card game, or? ;)

Here is my try. Because you don't want a value to be picked again, you need to have a set of values as a base. Each time you pick a number, you have to remove it from the base set. With the random number you determine the index of your base array, not the value itself.


' Empty ThinBASIC GUI file template
DIM w, y, z AS LONG
DIM x( 10 ) AS LONG
DIM base(10) AS LONG 'This is the base array where we pick numbers from
DIM msg AS STRING

dim count as long = 10
'w = 1

RANDOMIZE

ARRAY ASSIGN x( ) = 11
array assign base() = 1,2,3,4,5,6,7,8,9,10

'msg = "Y before"& $CR
'FOR y = 1 TO 10
' msg = msg & $CR & x( y )
'NEXT
'MSGBOX 0, msg

'msg = "Base before"& $CR
'FOR y = 1 TO 10
' msg = msg & $CR & base( y )
'NEXT
'MSGBOX 0, msg


FOR y = 1 TO 10
z = INT( RND( 1, count )) 'Get a random index for the base array
x(11-count) = base(z) 'fill the target array with the value of the indexed base array
base(z) = 0 'Set it to zero so the next sort will bring that value to the end
ARRAY SORT base( ), descend 'Descend sorting so the array then will be smaller, value wise

count -= 1 'Decrease the amount of array indices that can be choosen from
NEXT

'ARRAY SORT x( )


msg = "Y after"& $CR
FOR y = 1 TO 10
msg = msg & $CR & x( y )
NEXT
MSGBOX 0, msg

'msg = "Base after"& $CR
'FOR y = 1 TO 10
' msg = msg & $CR & base( y )
'NEXT
'MSGBOX 0, msg

ErosOlmi
10-09-2008, 22:05
And this is another way.
It generates m unique random numbers out of 4000 possible numbers.
It can be adapted to other possible cases.

Maybe I can make a native thinBasic function out there.




'The below code gets 20 unique rnds from 1 to 4000.

FUNCTION TBMAIN() as long
LOCAL randoms() AS LONG
LOCAL Integers() AS LONG
LOCAL i AS LONG, j AS LONG, ldup AS LONG, k AS LONG
LOCAL uniqueRndsFound AS LONG
LOCAL sTemp AS STRING
LOCAL n, m as long

m=20 'we only need 20 it seems
n=4000
REDIM integers(m)
REDIM Randoms(n)

RANDOMIZE TIMER 'only call this once for much better randomness, so put it outside loop

DO
k = RND(1, 4000) 'random from 1 to 4000
IF randoms(k) = 0 THEN
randoms(k) = 1 'keep track of which of the 4000 we've already used
INCR uniqueRndsFound 'add up until we get to 20
integers(uniqueRndsFound) = k 'store the rnd 1-4000 in integers()
IF uniqueRndsFound = 20 THEN EXIT DO 'exit as soon as we get 20 unique rnds
END IF
LOOP

stemp=""
FOR i=1 TO m
stemp=stemp+ FORMAT$(i, "00") & ") " & FORMAT$(integers(i)) & $CRLF
NEXT i
MSGBOX 0, stemp

ARRAY SORT integers()
stemp=""
FOR i=1 TO m
stemp=stemp+ FORMAT$(i, "00") & ") " & FORMAT$(integers(i)) & $CRLF
NEXT i
MSGBOX 0, stemp

END FUNCTION

Michael Hartlef
10-09-2008, 22:08
I think there is something wrong with ARRAY SCAN. Mark's first version should have worked. I just extended the full syntax of it and now it works with Mark's code too:

Nothin's wrong, Marc needed to set W to a value so the do loop was executed.


DIM w, y, z AS LONG
DIM x( 10 ) AS LONG
DIM msg AS STRING

w = 1

RANDOMIZE

ARRAY ASSIGN x( ) = 11

FOR y = 1 TO 10
z = INT( RND( 1, 10 ))
w=11 '<-------------- this is needed so the DO LOOP will be executed
DO UNTIL w = 0
w = ARRAY SCAN x( ) , = z
'msgbox 0,"W:"&w&" Z:"&z
IF w <> 0 THEN
'msgbox 0,"Allready there:"&w
z = INT( RND( 1, 10 ))
endif
LOOP
x( y ) = z
NEXT

'ARRAY SORT x( )

FOR y = 1 TO 10
msg = msg & $CR & x( y )
NEXT

MSGBOX 0, msg

marcuslee
10-09-2008, 22:27
Nothin's wrong, Marc Mark needed to set W to a value so the do loop was executed.


What's funny is I did set the value of W = 1 near the top of the script. Was it the fact that you set it to 11 instead of 1?

EDIT: I just figured out why the w=11 inside the FOR loop worked. When the loop reiterates, the w has a value of 0, which means the DO loop won't work anymore. Am I right? I feel so dumb and slow! :-[


Anyway, I'm glad it works. I wanted to figure this out pretty early on because I'm going to use this method in my flash card program that I'm forever in the process of creating.

Mark ;D

ErosOlmi
10-09-2008, 22:29
ARRAY SCAN seems working fine to me. Your example is working fine

Mark first example is not correct because when you change X() inside the DO/LOOP with a new random number, you have to start again the FOR/NEXT loop and check again the new number against all number from position 1 and not from the current position. That's why Mark mentioned it was taken from an example using GOTO.

ErosOlmi
10-09-2008, 22:30
So something like:



DIM y, z AS LONG
DIM x( 10 ) AS LONG
DIM msg AS STRING

RANDOMIZE

ARRAY ASSIGN x( ) = 11

FOR y = 1 TO 10
x( y ) = INT( RND( 1, 10 ))
FOR z = 1 TO ( y - 1 )
DO UNTIL x( z ) <> x( y )
x( y ) = INT( RND( 1, 10 ))
z = 0 '---A trck to restart the FOR from 1. Z will be incremented by NEXT immediately after the EXIT DO
exit do
LOOP
NEXT
NEXT

ARRAY SORT x( )

FOR y = 1 TO 10
msg = msg & $CR & x( y )
NEXT

MSGBOX 0, msg

ErosOlmi
10-09-2008, 22:34
Using thinDebug for those little scripts helps a lot.

Open the script into thiAir, press F8. Than use F8 to step by step or use menu to set break points.

Ciao
Eros

ErosOlmi
10-09-2008, 22:46
I just figured out why the w=11 inside the FOR loop worked. When the loop reiterates, the w has a value of 0, which means the DO loop won't work anymore. Am I right


Please note that thinBasic is very flexible with DO/LOOP loops. They can be written:



DO UNTIL x( z ) <> x( y )
'---Do something
LOOP


or



DO
'---Do something
LOOP UNTIL x( z ) <> x( y )


In first case logical test is performed before entering the loop so it can prevent to enter it.
In the second case logical test is performed after so at least once all the code inside the loop will be performed.

Ciao
Eros

Michael Clease
11-09-2008, 00:11
Mark here is a method I produced for a matchup game.



SUB RandomLevel()
LOCAL N as WORD VALUE = 1
LOCAL l(%TotalNumbers) as LONG
LOCAL m(%TotalNumbers) as LONG
LOCAL a,b,p,q as LONG VALUE = 1

' Fill an array with n Numbers
For n = 1 to (%TotalNumbers) : l(n) = n : NEXT
RANDOMIZE ' reseed the randomiser
DO
p = RND(1,%TotalNumbers) ' Generate a random number
For n = 1 to %TotalNumbers ' loop n times
if l(p) = 0 THEN EXIT FOR ' if the random number has been used generate a new number
m(q) = p : l(p) = 0 ' Store the random number and clear index
q += 1 ' Increase the counter
NEXT
LOOP UNTIL q => %TotalNumbers+1

marcuslee
11-09-2008, 00:52
z = 0
'---A trck to restart the FOR from 1.
'Z will be incremented by NEXT immediately after the EXIT DO




The power of 3 little characters. That is amazing. It is truly cool what you can accomplish with just a little bit of code. And, it is amazing how many different ways you can do the same thing. All of them have a few things in common, but they are all different as well.

Congrats, everyone.

Mark ;D