jeudi 24 décembre 2015

How to generate a list of random numbers without duplicates in pure batch scripting?

I want to generate a list of random numbers with a predefined number of items %RND_TOTAL% in the given range from %RND_MIN% to %RND_MAX% and with a certain interval %RND_INTER%. Of course this can be accomplished with the following code snippet:

@echo off
setlocal EnableExtensions EnableDelayedExpansion
rem total number of random numbers:
set /A "RND_TOTAL=8"
rem range for random numbers (minimum, maximum, interval):
set /A "RND_MIN=1, RND_MAX=10, RND_INTER=1"
rem loop through number of random numbers:
for /L %%I in (1,1,%RND_TOTAL%) do (
    rem compute a random number:
    set /A "RND_NUM[%%I]=!RANDOM!%%((RND_MAX-RND_MIN)/RND_INTER+1)*RND_INTER+RND_MIN"
    echo !RND_NUM[%%I]!
)
endlocal
exit /B

Here is an example of the corresponding output (4 and 9 both apear twice here):

2
4
9
8
9
4
7
3

But how can I create such a list without any duplicate items?


Of course I could use the following script which checks each item whether it is already avalable in the array-like variable RND_NUM[], but this approach is quite inefficient due to nested for /L loops and, particularly when %RND_TOTAL% comes close to the available count of random numbers covered by the the range specification, due to numerous calculation repetitions:

@echo off
setlocal EnableExtensions EnableDelayedExpansion
rem total number of random numbers, duplicate flag (`0` means no duplicates):
set /A "RND_TOTAL=20, FLAG_DUP=0"
rem range for random numbers (minimum, maximum, interval):
set /A "RND_MIN=1, RND_MAX=30, RND_INTER=1"
rem loop through number of random numbers, generate them in a subroutine:
for /L %%I in (1,1,%RND_TOTAL%) do (
    call :SUB %%I
    echo !RND_NUM[%%I]!
)
endlocal
exit /B

:SUB
rem get number of already collected random numbers:
set /A "RND_COUNT=%1-1"
:LOOP
rem compute a random number:
set /A "RND_NUM[%1]=!RANDOM!%%((RND_MAX-RND_MIN)/RND_INTER+1)*RND_INTER+RND_MIN"
rem check whether random number appears in the previous collection:
if %FLAG_DUP% EQU 0 (
    for /L %%I in (1,1,%RND_COUNT%) do (
        rem re-compute random number if duplicate has been encountered:
        if !RND_NUM[%1]! EQU !RND_NUM[%%I]! (
            goto :LOOP
        )
    )
)
exit /B

This is the related sample output (no duplicates here):

4
1
2
10
6
7
3
5




Aucun commentaire:

Enregistrer un commentaire