DarkBoost Posted February 10, 2009 Share Posted February 10, 2009 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 More sharing options...
ds34 Posted February 10, 2009 Share Posted February 10, 2009 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 More sharing options...
DarkBoost Posted February 10, 2009 Author Share Posted February 10, 2009 I think I understand what you are saying however it still duplicates numbers. Dim $var[4] For $z = 1 To 3 $var[$z] = Random(1,10,1) Next MsgBox(0,"Random Numbers","Number01: "&$var[1]&@CRLF&"Number02: "&$var[2]&@CRLF&"Number03: "&$var[3]) Link to comment Share on other sites More sharing options...
ds34 Posted February 10, 2009 Share Posted February 10, 2009 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 More sharing options...
DarkBoost Posted February 10, 2009 Author Share Posted February 10, 2009 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 More sharing options...
99ojo Posted February 10, 2009 Share Posted February 10, 2009 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 More sharing options...
BrettF Posted February 10, 2009 Share Posted February 10, 2009 (edited) 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 February 10, 2009 by BrettF Vist my blog!UDFs: Opens The Default Mail Client | _LoginBox | Convert Reg to AU3 | BASS.au3 (BASS.dll) (Includes various BASS Libraries) | MultiLang.au3 (Multi-Language GUIs!)Example Scripts: Computer Info Telnet Server | "Secure" HTTP Server (Based on Manadar's Server)Software: AAMP- Advanced AutoIt Media Player | WorldCam | AYTU - Youtube Uploader Tutorials: Learning to Script with AutoIt V3Projects (Hardware + AutoIt): ArduinoUseful Links: AutoIt 1-2-3 | The AutoIt Downloads Section: | SciTE4AutoIt3 Full Version! Link to comment Share on other sites More sharing options...
DarkBoost Posted February 10, 2009 Author Share Posted February 10, 2009 @99ojo nice script, works well... never used Ubound before! Link to comment Share on other sites More sharing options...
bobchernow Posted February 11, 2009 Share Posted February 11, 2009 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 More sharing options...
DarkBoost Posted February 11, 2009 Author Share Posted February 11, 2009 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 More sharing options...
bobchernow Posted February 11, 2009 Share Posted February 11, 2009 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 More sharing options...
Spiff59 Posted February 11, 2009 Share Posted February 11, 2009 (edited) 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 February 11, 2009 by Spiff59 Link to comment Share on other sites More sharing options...
bobchernow Posted February 11, 2009 Share Posted February 11, 2009 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 More sharing options...
Spiff59 Posted February 11, 2009 Share Posted February 11, 2009 Actually, the engine of my example is one not yet posted. For a list of 100 numbers, it calls Random() 100 times. Link to comment Share on other sites More sharing options...
bobchernow Posted February 11, 2009 Share Posted February 11, 2009 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 More sharing options...
Spiff59 Posted February 11, 2009 Share Posted February 11, 2009 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 More sharing options...
Xand3r Posted February 11, 2009 Share Posted February 11, 2009 (edited) @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 February 11, 2009 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 More sharing options...
DarkBoost Posted February 11, 2009 Author Share Posted February 11, 2009 Wow some very impressive formulas I thank you all for sharing. If people wouldn't mind adding a few comments to the examples that would be very much appreciated Link to comment Share on other sites More sharing options...
Xand3r Posted February 11, 2009 Share Posted February 11, 2009 (edited) #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 February 11, 2009 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 More sharing options...
Spiff59 Posted February 11, 2009 Share Posted February 11, 2009 (edited) @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 February 12, 2009 by Spiff59 Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now