1.

Solve : Choosing random numbers without repeats.?

Answer»

How could you make a batch file choose random numbers (for example 1-20) without repeating each other? Also, how can it 'detect' that it picked ALL of the numbers then go somewhere else (goto :blank)?

I know how to choose random numbers:
Code: [Select]echo off
:1
cls
set /a R=%random%%% 20 +1
echo %R%
pause >nul
goto 1

But theres some problems in this. It repeats numbers, and doesn't go anywhere if/when it detects all of the numbers have been used (the two main points in the BEGINNING)

Please help   

Quote

It repeats numbers, and doesn't go anywhere if/when it detects all of the numbers have been used (the two main points in the beginning)

It repeats numbers because past performance is no indicator of future results. Each resolution of random is an independent event and has no "memory" of what numbers were chosen previously.

I'm not seeing in your code where it detects when all the numbers have been used. You will need to keep a history of numbers displayed and if there is a duplicate, re-randomize until a non-duplicate is found.  You could use an array in Powershell to hold the history or the dictionary object in VBScript.  Other script languages have similar features.

Good luck. 

Note: using pause > nul without any indication to the user of what to do, will not win you any gold stars. The way I'd do it in a more capable language would be to create an array of all the items that can be selected, and when a random item is chosen, choose an index into that array randomly, use the value in the index as the random value, and then remove the item from that list.

This makes choosing a random number linear with regards to how many items have been previously selected, whereas if you were to store each selected item, you would have to go through all the stored items and make sure it's not the new choice; what this would mean is that, if you had 100 values and you  had already chosen 99, whereas the method I propose would instantly give you back that last number, the more "brute-force" approach would probably take a very long time to finally come up with the only POSSIBLE solution. Code: [Select]echo off
setlocal enabledelayedexpansion
set maxnum=20
if exist AlreadyChosen.txt del AlreadyChosen.txt
:loop
set /a RandNum=%random%%%%maxnum%+1
set alreadychosen=0
if exist AlreadyChosen.txt for /f "delims=" %%A in (AlreadyChosen.txt) do (if "%%A"=="%RandNum%" set alreadychosen=1)
if %alreadychosen% equ 0 (>> AlreadyChosen.txt echo %RandNum%)
set NumbersStored=0
for /f "delims=" %%A in (AlreadyChosen.txt) do set /a NumbersStored=!NumbersStored!+1
if %NumbersStored% lss %maxnum% goto loop
echo Have now chosen every single number between 1 and %maxnum% once each.

times (3.0 GHz AMD Phenom II, 7200 rpm HDD)

Code: [Select]maxnum       seconds
10           0.09 sec
20           0.25 sec
100          6.35 sec
200          32.48 sec
500          201.51 sec


Quote from: Salmon Trout on December 02, 2010, 12:04:26 PM
Code: [Select]echo off
setlocal enabledelayedexpansion
set maxnum=20
if exist AlreadyChosen.txt del AlreadyChosen.txt
:loop
set /a RandNum=%random%%%%maxnum%+1
set alreadychosen=0
if exist AlreadyChosen.txt for /f "delims=" %%A in (AlreadyChosen.txt) do (if "%%A"=="%RandNum%" set alreadychosen=1)
if %alreadychosen% equ 0 (>> AlreadyChosen.txt echo %RandNum%)
set NumbersStored=0
for /f "delims=" %%A in (AlreadyChosen.txt) do set /a NumbersStored=!NumbersStored!+1
if %NumbersStored% lss %maxnum% goto loop
echo Have now chosen every single number between 1 and %maxnum% once each.

Wow that's almost exactly what i wanted/needed. Thanks a lot But one little tweak i hope you can do is not make a .txt file with the numbers, but actually show them instead (on batch screen).

Also, if you could make it show on the batch screen, is there a way to put a pause between each number? For example it picks a random number from 1-20 and shows it, and when you press any key it will show the next number and so on.I will do some more tomorrow; it is 11 PM now and I am working tomorrow; look for a post in around 18 hours
Code: [Select]echo off
setlocal enabledelayedexpansion
set maxnum=20
if exist AlreadyChosen.txt del AlreadyChosen.txt
:loop
set /a RandNum=%random%%%%maxnum%+1
set alreadychosen=0
if exist AlreadyChosen.txt for /f "delims=" %%A in (AlreadyChosen.txt) do (if "%%A"=="%RandNum%" set alreadychosen=1)
if %alreadychosen% equ 0 (
>> AlreadyChosen.txt echo %RandNum%
echo %RandNum%
echo press a key for another number
pause>nul
)
set NumbersStored=0
for /f "delims=" %%A in (AlreadyChosen.txt) do set /a NumbersStored=!NumbersStored!+1
if %NumbersStored% lss %maxnum% goto loop
echo Have now chosen every single number between 1 and %maxnum% once each.
Thanks a lot! Works great!I have been playing around with this. I believe any non-random-access method will slow down exponentially as the max number increases. This is what you might expect. With a sequential access method the more numbers you have already chosen, the longer it will take on average to verify that each random number chosen is a new one. The method repeatedly scanning a single text file is quite slow. Next I tried creating an empty string VARIABLE and appending each chosen number to it, bracketing each one with separators e.g. 30 19 2 15 and using FIND separator%randnum%separator to decide if a new random number was already chosen. This is dramatically slower than the previous method for smaller values of maxnum but there is a point where it gets faster (on my system it is occurs somewhere between 250 - 500 with the disk I am using.) Finally I tried creating a subfolder and creating a small file in it named for each number chosen, e.g.

echo.>tempfolder\%randnum%

Then you can exploit file system random access and use IF EXIST tempfolder\%randnum% to see if a new number has already been chosen. This is much faster than either of the previous methods.

Time in seconds

Maxnum    textfile   string  filenames
 10        0.07       0.11     0.09
 50        1.01       5.32     0.35
100        4.18       9.88     0.73
250       26.65      38.07     1.80
500      145.46      86.79     4.34

[edit] I can't believe I have spent an hour on this! It underlines that for anything more than toy stuff, you need to learn a proper programming language.




I found this in the snippet CLOSET. Originally written in VBScript using the dictionary object, this uses a compound naming convention for the variables. Basically it is another approach to the solution.

The first part of the variable name is the word slot. The second portion is the random number generated as is the value of the variable. This produces variables such as slot.20 with a value of 20 or slot.15 with a value of 15. Using such a scheme allows the code to simply check if the variable is defined and eliminates the file system processing.

As previously pointed out, the code slows down as the pool of unused random numbers shrinks. I made the code generic by allowing the user to enter the min and max numbers of the number range. I also eliminated the PROMPT to get the next number, but the code is remarked out and can easily be reactivated.

Code: [Select]echo off
::
:: No error check that max num is greater than min num
::
setlocal

set /p intLowNumber=Enter Minimum Number:
set /p intHighNumber=Enter Maximum Number:

set /a intMin=%intLowNumber%
set /a intMax=%intHighNumber%
set /a count=%intLowNumber%

:loop
  if %count% GTR %intHighNumber% goto :eof
  set /a rnd=%random% %% (%intHighNumber% - %intLowNumber% + 1) + %intLowNumber%
  if not defined slot.%rnd% (
    set slot.%rnd%=%rnd%
    set /a count+=1
    echo %rnd%
    rem echo Press A Key For Next Number
    rem pause>nul
  )
  goto loop

 Sidewinder's method is fastest by far.


Discussion

No Comment Found