Jump to content

Random


 Share

Recommended Posts

I am trying to use the random command without it repeating the number it has already selected or another method for randomising a number without it being repeated.

I could obviously limit the Random criteria eg. (1,3) then (4,7) then (8,10) however this restricts the results which I want to avoid.

MsgBox(0,"Random Numbers","Number01: "&Random(1,10,1)&@CRLF&"Number02: "&Random(1,10,1)&@CRLF&"Number03: "&Random(1,10,1))
Link to comment
Share on other sites

In this case you'll need to store the already generated random numberes in a array.

Then repeat the random call in a loop until the result of this is not already present in the array.

Once a new random number is meeting above statement add it to the array as well.

Link to comment
Share on other sites

Try something like this:

CODE
#Include <Array.au3>

Dim $aUsedNumbers[1]

While 1

While 1

$newNumber=Random(1,10,1)

If UsedNumberCheck($aUsedNumbers,$newNumber)=-1 Then

_ArrayAdd($aUsedNumbers,$newNumber)

ExitLoop

EndIf

WEnd

If MsgBox(1,"New Random Number",$newNumber)=2 Then Exit

_ArrayDisplay($aUsedNumbers,"Old Random Numbers")

WEnd

Func UsedNumberCheck($aTest,$iTestNumber)

Local $loop

For $loop=0 To UBound($aTest)-1

If $aTest[$loop]=$iTestNumber Then Return 1

Next

Return -1

EndFunc

You will see, that the program run endlessly in the inner While loop when all numbers are used up. So you'll need to handle this as well...

Link to comment
Share on other sites

This seems to work but maybe not the best method?

Dim $var[4]

For $z = 1 To 3
    $var[$z] = Random(1,10,1)
Next

While 1
    If $var[2] = $var[1] Then 
        $var[2] = Random(1,10,1) 
    ElseIf $var[3] = $var[1] Then 
        $var[3] = Random(1,10,1)
    ElseIf $var[3] = $var[2] Then 
        $var[3] = Random(1,10,1)
    Else 
        ExitLoop
    EndIf
WEnd

MsgBox(0,"Random Numbers","Number01: "&$var[1]&@CRLF&"Number02: "&$var[2]&@CRLF&"Number03: "&$var[3])
Link to comment
Share on other sites

Hi,

more fexible solution for some more numbers:

#Include <Array.au3>

Dim $var[10], $i = 2

$var [1] = Random (1,UBound ($var),1)
While $i < UBound ($var)
    $rand = Random (1,Ubound ($var),1)
    $index = _ArraySearch ($var, $rand, 1)
    if $index < 0 Then
        $var [$i] = $rand
        $i = $i + 1
    EndIf
WEnd
_ArrayDisplay ($var)

;-))

Stefan

Link to comment
Share on other sites

This is a slightly different take on it... :)

Global $tempstr = ""
Global $count = 0
Global $total = 10
Global $aRnd[$total + 1]
Global $min = 1
Global $max = 10

While $count <> $total
    $tempnumber = Random($min, $max, 1)
    If Not StringInStr ($tempstr, $tempnumber & "|", 1) Then
        $count += 1
        $aRnd[$count] = $tempnumber
        $tempstr &= $tempnumber & "|"
    EndIf
WEnd

$aRnd[0] = $count
_ArrayDisplay ($aRnd)

EDIT: Faster again...

#Include <Array.au3>
$timer1 = TimerInit()
Global $tempstr = ""
Global $count = 0
Global $total = 10
Global $min = 1
Global $max = 10

While $count <> $total
    $tempnumber = Random($min, $max, 1)
    If Not StringInStr ($tempstr, $tempnumber & "|", 1) Then
        $count += 1
        $tempstr &= $tempnumber & "|"
    EndIf
WEnd

$aRnd = StringSplit (StringTrimRight ($tempstr, 1), "|")
$timer1 = TimerDiff ($timer1)
_ArrayDisplay ($aRnd)

MsgBox (0, "", "Test 1: " & $timer1)

Cheers,

Brett

Edited by BrettF
Link to comment
Share on other sites

Here is a method that avoids doing any real searching at all. It uses a separate associative array that is initialized to -1. Then when a number is used, the element at that index is marked used. So now on each call to Random the test is simply is that element in the array used already. I have not cleaned it up to deal with 0 based arrays but it should show the technique.

#include <Array.au3>

Global $limit = 11
Dim $control[$limit]
Dim $var[$limit]
Global $next = 1

For $i = 0 To $limit - 1
    $control[$i] = -1
Next

While $next < $limit
    $new = Random(1, 10, 1)
    If $control[$new] == -1 Then
        $var[$next] = $new
        $control[$new] = $new
        $next += 1
    EndIf
WEnd


_ArrayDisplay($var)

The Array.au3 include is only needed for pretty output.

Bob

--------------------bobchernow, Bob ChernowWhat a long strange trip it's beenUDFs: [post="635594"]Multiple Monitor Screen Resolution Change[/post]

Link to comment
Share on other sites

Hi,

more fexible solution for some more numbers:

#Include <Array.au3>

Dim $var[10], $i = 2

$var [1] = Random (1,UBound ($var),1)
While $i < UBound ($var)
    $rand = Random (1,Ubound ($var),1)
    $index = _ArraySearch ($var, $rand, 1)
    if $index < 0 Then
        $var [$i] = $rand
        $i = $i + 1
    EndIf
WEnd
_ArrayDisplay ($var)

;-))

Stefan

This is probably best suited for my scenario but there are few commands I have not used before so it makes it hard to understand what it is doing.

I have tried to rewrite this without the variables to assist and wondering if the below would be correct?

While 2 < 10
    If _ArraySearch(10,Random(1,10,1)) < 0 Then
        2 = Random(1,10,1)
        2 + 1
    EndIf
WEnd

_ArrayDisplay(10)
Link to comment
Share on other sites

Here is a method that avoids doing any real searching at all. It uses a separate associative array that is initialized to -1. Then when a number is used, the element at that index is marked used. So now on each call to Random the test is simply is that element in the array used already. I have not cleaned it up to deal with 0 based arrays but it should show the technique.

#include <Array.au3>

Global $limit = 11
Dim $control[$limit]
Dim $var[$limit]
Global $next = 1

For $i = 0 To $limit - 1
    $control[$i] = -1
Next

While $next < $limit
    $new = Random(1, 10, 1)
    If $control[$new] == -1 Then
        $var[$next] = $new
        $control[$new] = $new
        $next += 1
    EndIf
WEnd


_ArrayDisplay($var)

The Array.au3 include is only needed for pretty output.

Bob

New version, a little cleaner with timing info.

#include <Array.au3>
$timer1 = TimerInit()
Global $limit = 100
Dim $control[$limit]
Dim $var[$limit]
Global $next = 1

For $i = 0 To $limit - 1
    $control[$i] = 0
Next

While $next < $limit
    $new = Random(1, $limit-1, 1)
    If Not $control[$new] Then
        $var[$next] = $new
        $control[$new] = $new
        $next += 1
    EndIf
WEnd
$timer1 = TimerDiff ($timer1)
MsgBox (0, "", "Test 1: " & $timer1)
_ArrayDisplay($var)

--------------------bobchernow, Bob ChernowWhat a long strange trip it's beenUDFs: [post="635594"]Multiple Monitor Screen Resolution Change[/post]

Link to comment
Share on other sites

This is rather sweet (and blazingly fast) ...

#include <Array.au3>
Global $limit = 100
Dim $control[$limit+1]
Dim $var[$limit+1]

$timer1 = TimerInit()
For $i = 0 To $limit
    $control[$i] = $i Next
For $i= 0 to $limit
    $new = Random(1, $limit, 1)
    $var[$i] = $control[$new]
    $control[$new] = $control[$limit]
    $limit -= 1
Next
$timer1 = TimerDiff ($timer1)

MsgBox (0, "", "Test 1: " & $timer1)
_ArrayDisplay($var)

Edit: removed a "+1", was returning an array one larger than set by $limit

Edited by Spiff59
Link to comment
Share on other sites

This is rather sweet (and blazingly fast) ...

#include <Array.au3>
Global $limit = 100
Dim $control[$limit+1]
Dim $var[$limit+1]

$timer1 = TimerInit()
For $i = 0 To $limit
    $control[$i] = $i + 1
Next
For $i= 0 to $limit
    $new = Random(1, $limit, 1)
    $var[$i] = $control[$new]
    $control[$new] = $control[$limit]
    $limit -= 1
Next
$timer1 = TimerDiff ($timer1)

MsgBox (0, "", "Test 1: " & $timer1)
_ArrayDisplay($var)

Thanks. I came across this method back when I used to write a lot of REXX code. It has the advantage that it will scale up with almost no penalty. The only real time consumption is all the calls to Random. I also noticed something that all of these algoritms have a common problem with. It seems that for certain numbers of Max, the Random function may never pick a number. On one run in the debugger with a max of 15, I saw that 14 of the numbers eventually showed up but for sume reason, no matter how many times I ran Random it NEVER picked the number 3. That causes a hang since the algoritm waits for all numbers to be picked before ending. But then again, that is just in this test usage. You would normally not wait for every number to be exhausted. Mine also has a limitation that the numbers must be integers.

Bob

--------------------bobchernow, Bob ChernowWhat a long strange trip it's beenUDFs: [post="635594"]Multiple Monitor Screen Resolution Change[/post]

Link to comment
Share on other sites

Actually, the engine of my example is one not yet posted.

For a list of 100 numbers, it calls Random() 100 times.

My mistake. I though your comment was on mine, but I ran your code and I think it is quite elegant and very fast. I love how your swapping allows only a single call to Random per loop. Sweet.

--------------------bobchernow, Bob ChernowWhat a long strange trip it's beenUDFs: [post="635594"]Multiple Monitor Screen Resolution Change[/post]

Link to comment
Share on other sites

Thank you, Bob. I did certainly reuse your example and just change the mechanics.

The head start is much appreciated!

I saw the OP post, regarding a previous example, a question as to what it was actually doing, so in case he'd find an explanation of use:

This one loads an array with the values 1 through $limit

But, what goes into the output array is not the value returned by the Random() statement, but the value contained in the element of the array that the random() statement points to. It moves the value of the randomly selected element into the first slot of the output array, then moves the last value in the array to the slot just chosen, then sets $limit down by one. So, the first pass, if say "33" comes up, that element contains "33" and is placed into the output list, then "100" will be moved into element 33, and the scope of the next Random() is set down one. The next Random() call, to fill output slot 2, will only consider using the values found in the first 99 elements of the table (everything except 33). 100 passes through the loop, with 100 calls to Random(), and you get all 100 unique numbers.

Link to comment
Share on other sites

@Spiff59 good func but slow when using big numbers... and even slower if you make it a func and use it several times for the simple fact that lets say you want random numbers between 1 and 1000000 then you will have to pad numbers from 1 to 1000000 every function call... and that consumes lots of processing time.

A simpler and at least twice as fast approach:

#include<Array.au3>
$results=200000
$min=10
$max=10000000
Dim $array[$max-$min+1];save precious memory space :)
Dim $_results[$results+1]
$timer=TimerInit()
For $i=1 To $results
    Do
        $_new=Random($min,$max,1)
    Until $array[$_new-$min]=""
    If Mod($i,10000)=0 Then ToolTip($i , 0 ,0)
    $array[$_new-$min]=1
    $_results[$i]=$_new
Next
$done=TimerDiff($timer)
$msg=MsgBox(1 , "", "made "&$results&" random number generations in "&$done&" ms"&@CRLF&"Press ok to list them(array display takes a very long time with values over 20000)")
If $msg=1 Then _ArrayDisplay($_results,$done)

what this does is just declare the array and put 1 on the array slow if it's used

Edited by TheMadman

Only two things are infinite, the universe and human stupidity, and i'm not sure about the former -Alber EinsteinPractice makes perfect! but nobody's perfect so why practice at all?http://forum.ambrozie.ro

Link to comment
Share on other sites

#include<Array.au3>;obvious... for the array display
$results=200000;number of random numbers to generate must be bigger than $max-$min for obvious reasons
$min=10;min number for random number generation
$max=10000000;max  number for random number generation
Dim $array[$max-$min+1];save precious memory space ;declare the array that will tell us if a number has already been picked
Dim $_results[$results+1] ;array to store results
$timer=TimerInit();timer
For $i=1 To $results;start generating the random numbers
    Do
        $_new=Random($min,$max,1) ; generate a random number
    Until $array[$_new-$min]="" ; do it until we generate one that wasn't generated so far
    If Mod($i,10000)=0 Then ToolTip($i , 0 ,0);debug :P
    $array[$_new-$min]=1;we found a new number make sure it won't get picked again
    $_results[$i]=$_new;add it to the results array
Next;done looping
$done=TimerDiff($timer);how much time has passed?
$msg=MsgBox(1 , "", "made "&$results&" random number generations in "&$done&" ms"&@CRLF&"Press ok to list them(array display takes a very long time with values over 20000)");show info
If $msg=1 Then _ArrayDisplay($_results,$done) ; was ok pressed?

cheers

Edited by TheMadman

Only two things are infinite, the universe and human stupidity, and i'm not sure about the former -Alber EinsteinPractice makes perfect! but nobody's perfect so why practice at all?http://forum.ambrozie.ro

Link to comment
Share on other sites

@Spiff59 good func but slow when using big numbers... and even slower if you make it a func and use it several times for the simple fact that lets say you want random numbers between 1 and 1000000 then you will have to pad numbers from 1 to 1000000 every function call... and that consumes lots of processing time.

A simpler and at least twice as fast approach:

#include<Array.au3>
$results=200000
$min=10
$max=10000000
Dim $array[$max-$min+1];save precious memory space :)
Dim $_results[$results+1]
$timer=TimerInit()
For $i=1 To $results
    Do
        $_new=Random($min,$max,1)
    Until $array[$_new-$min]=""
    If Mod($i,10000)=0 Then ToolTip($i , 0 ,0)
    $array[$_new-$min]=1
    $_results[$i]=$_new
Next
$done=TimerDiff($timer)
$msg=MsgBox(1 , "", "made "&$results&" random number generations in "&$done&" ms"&@CRLF&"Press ok to list them(array display takes a very long time with values over 20000)")
If $msg=1 Then _ArrayDisplay($_results,$done)

what this does is just declare the array and put 1 on the array slow if it's used

Quite a bit of "spin" in that post. The example you're picking on is 1000% faster than the one you posted regardless of the size of the array requested, or how many times called. You're bending the rules by asking yours to only return 20% of the numbers in the list, which greatly reduces the number of redundant calls to Random(). Tell that routine to return 1 million of 1 million numbers in a randomized array, and come back tomorrow, it may be finished.

Edit: Oops! I got my 0's mixed up... the "twice as fast" routine is even more of a fraud than I first noticed. You're only returning 2%, not 20%, of the possible numbers available to make a "bang on Random() until the slots are full" routine look fast. And you're pumping up the range to a ludicrous 10 million to make the routine I posted suffer (marginally) from initializing such a large array. Tacky.

Edited by Spiff59
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...