I need a batch file randomizer to randomly select 5 numbers from 1-30 then output them. I have researched and found nothing that is useful to me. I am a Beginner so if you could make the code simple and explain it so i can reproduce it later if i need it again.
batch noob answer is absolutly correct, but can generate duplicated numbers. For an option with no duplicate numbers,
#echo off
setlocal enableextensions
for /F "tokens=2" %%l in ('cmd /v:on /c "#for /L %%n in (1 1 30) do #echo ^!random^! %%n"^|sort^|more +25') do (
echo %%l
)
It spawns a cmd (cmd /v:on /c "....") to generate a list of numbers 1-30 prefixed with a random number, this list is then sorted (sort), using the random number as key. Once sorted, more +25 is used to skip the first 25 numbers in list, and then, a for /F is used to step over the 5 last lines, taking the second token from it (the lines contains the random number and the number from 1-30) and echoing it to console
Use RANDOM -- that's what you are looking for.
RANDOM gives a number between 0 and 32767.
Example:
set /a num=%random%
echo.%num%
This will output a number between 0 and 32767.
But if you want to reduce that number do this:
set /a num=%random% %%5
echo.%num%
This will output a number between 0 and 4.
In your case (range 1 to 30) do this:
set /a num1=%random% %%30 +1
set /a num2=%random% %%30 +1
set /a num3=%random% %%30 +1
set /a num4=%random% %%30 +1
set /a num5=%random% %%30 +1
echo %num1%
echo %num2%
echo %num3%
echo %num4%
echo %num5%
Hope it helps! :D
Related
I need to create an integer array that stops at a limit number and then goes on after another fixed value. Something like [1 2 3 4 5 10 11 12 13 14 15 16], where 5 is the limit number and 10 where it restarts.
Can you do something like set arr = [1:%lim%, 10:%max%] ?
#ECHO OFF
SETLOCAL
CALL :buildlist mylist 1 5 10 16
ECHO list is %mylist%
PAUSE
GOTO :eof
:: Produce a list of integers
:: %1 is listname
:: %2 is start value
:: %3 is end-value for range
:: %4 is restart-value
:: %5 is end-value
:buildlist
SET "%1="
FOR /L %%v IN (%2,1,%5) DO (
IF %%v leq %3 CALL SET "%1=%%%1%% %%v"
IF %%v geq %4 CALL SET "%1=%%%1%% %%v"
)
CALL SET "%1=%%%1:~1%%"
GOTO :eof
Batch really doesn't have arrays, but can simulate one with a little imagination.
The above routine produces a list of values to the specification, ready for processing by a for statement.
Although it's possible to use delayedexpansion, it's possible to avoid that facility. By CALL ing a set command, the command is parsed before execution so to decode the hieroglyphics, where %1 is the variable name "var"
SET "%1=%%%1%% %%v"
is processed as:
SET "var=%var% %v"
as %% is an escaped-%, and %v will be replaced by the value of the metavariable (loop-control variable) %%v
Similarly,
SET "%1=%%%1:~1%%"
is executed as
SET "var=%var:~1%"
which deletes the first character, which will be a space.
This problem is about several for /L commands really, although it could be described in terms of nested if commands or in other different ways...
There are several different methods to give the values of the for commands, and also to implement they. I think this is the simplest one:
#echo off
setlocal EnableDelayedExpansion
set ranges="1:5" "10:16"
set "list="
for %%a in (%ranges::= 1 %) do for /L %%i in (%%~a) do set "list=!list! %%i"
echo list=%list%
Note that this method also works with several (more than two) ranges of values.
PS - This result is a list, not an array
Batch doesn't have OR operators, so you'll have to make do with two separate if statements - one for the first set of numbers and one for the second set of numbers. You can, however, put both of those in the same for loop:
#echo off
setlocal enabledelayedexpansion
set "lim=5"
set "max=10"
REM Setting counter to -1 because we're going to increment the counter and then
REM use it, and arrays famously start at zero.
set "counter=-1"
for /L %%A in (1,1,16) do (
if %%A LEQ %lim% (
set /a counter+=1
set "array[!counter!]=%%A"
)
if %%A GEQ %max% (
set /a counter+=1
set "array[!counter!]=%%A"
)
)
REM Display the contents of the array just to prove it worked.
REM Alphabetic order is used, so array[10] and array[11] are going to print before array[1]
set array[
I have a question about a piece of code I am writing.
Set /a 4DgtNum= %Random% %%9999
That's what is being used. I need to know how could I make a "Start at this number end in the range of another" code like start at (I.E 1000 and the max you can go to is 9999, but use any number between that ratio).
#ECHO Off
SETLOCAL enabledelayedexpansion
For %%b IN (#) DO FOR /F "delims==" %%a In ('set %%b 2^>Nul') DO SET "%%a="
FOR /L %%x IN (1,1,30000) DO CALL :choosernd&SET /a #!num!+=1
SET #
GOTO :eof
:choosernd
set /a min=1000
set /a max=9999
set /a min=1
set /a max=5
set /a rangesize=max-min+1
SET /a numranges=32767/rangesize
SET /a maxrange=rangesize * numranges-1
:chooseagain
set /a num=%RANDOM%
if %num% gtr %maxrange% GOTO chooseagain
SET /a num=(num %% rangesize) + min
rem ECHO %num%
GOTO :EOF
This question has been asked many times on SO. Here's a generate-and-test routine.
The meat of the matter is the :choosernd routine, where you set your minimum and maximum values.
I overrode your 1000 and 9999 with 1 and 5 for ease of testing.
First, calculate range size. That part should be obvious.
Next - the random-number generator returns %random% in the range 0..32767, so using %random% %% 100 for instance would return 328 numbers in 0..67, but 327 in 68..99. Hence, calculate the number of full ranges (327) available, and the maximum value to obtain an even distribution; (100*327)-1=32699.
Get a random number, but reject any that are greater than the maximum, so reject 32700..32767.
Perform a modulus operation on the chosen, filtered number and add the minimum, giving num
The code before this is simply clearing any variables whose names begin # and then executing the :choosernd routine 30,000 times. Each time, the variable #!num! is incremented, where delayedexpansion (which is explained many. many times on SO - use the search facility) is used to correctly choose a counter.
Then the counter is displayed for verification (the "random" generator is not particularly good)
Note that delayedexpansion is used only in the logging routine - it does not need to be invoked for the :choosernd routine.
Also - note that variablenames may not start with a digit.
When you use the point-click-and-giggle method of executing a batch, the batch window will often close if a syntax-error is found. You should instead open a 'command prompt' and run your batch from there so that the window remains open and any error message will be displayed.
So I'm working on a final project for one of my classes, and the goal is basically to have the user input an initial investment, and the number of years the value will depreciate...it depreciates by a constant value which is determined by the investment divided by the number of years.
In the for loop, it should subtract the 'annual' from the 'bookvalue' and print out the new bookvalue for each year...the math itself works correctly (I believe?) because if I echo the values outside my for loop they are what they should be, but with the echo statement inside the for loop I don't get what I need it to output (the bookvalue doesn't change inside the loop).
Not really sure where to go with this, and Google hasn't done me very well yet. Thanks for any help that can be given!
#ECHO OFF
:HEADER
REM Program : final_q8.bat
REM Author :
REM Date :
REM Purpose : if statement integer sort
REM -----------------------------------------------
:START
CLS
SET /P INVEST="Please enter the initial investment amount: "
SET /P YEARS="Please enter the # of years the machine will be depreciated: "
SET /A ANNUAL=INVEST/YEARS
SET BOOKVALUE=%INVEST%
ECHO YEAR ANNUAL DEPRECIATION BOOK VALUE
FOR /L %%C IN (1, 1, %YEARS%) DO (
SET /A BOOKVALUE=BOOKVALUE-ANNUAL
ECHO %%C %ANNUAL% %BOOKVALUE%
)
PAUSE
:END
:: Exit and return an errorcode of 0
EXIT /B 0
Try enabling Delayed expansion:
#echo off
setlocal enabledelayedexpansion
...
Then use exclamation mark inside For loop
FOR /L %%C IN (1, 1, %YEARS%) DO (
SET /A BOOKVALUE=BOOKVALUE-ANNUAL
ECHO %%C !ANNUAL! !BOOKVALUE!
)
As part of a program I'm making, I want to be able to 'plot' or 'mark' random points on a grid. In doing so I need to generate a random number(x) to determine the number of plots- and then x amount of different random numbers representing coordinates. At the moment I have the following code:
#echo off
:obno
set /a r1=%random%
if %r1% gtr 10 goto obno else (
goto ob
)
:ob
for /L %%R in (1,1,%r1%) do set /a n%%R=%random%*240/32678+1
echo %r1%
echo %n1% %n2% %n3% %n4% %n5% %n6% %n7% %n8% %n9% %n10%
pause
What happens here is I end up with x amount of the same random number instead of different ones
i.e. ouput:
5
108 108 108 108 108
so any help would be greatly appreciated!
In a loop you should be using delayed expansion e.g.
SETLOCAL ENABLEDELAYEDEXPANSION and using !RANDOM! instead of %RANDOM%
And...
%RANDOM% gives you a random number between 0 and 32767.
Using an expression like below you can change the range to anything you like (here the range is [1…100] instead of [0…32767]).
SET /A test=%RANDOM% * 100 / 32768 + 1
And below will produce number between 1~100
set /a num=%random% %%100 +1
To get a random number between 0 and 9 on the command line, use
set /a "rand=%random% % 10"
If used in a batch file then double the modulo operator
set /a "rand=%random% %% 10"
Using your code, you can use a call and double the % to make %%random%%
#echo off
:obno
set /a r1=%random%
if %r1% gtr 10 goto obno else (
goto ob
)
:ob
for /L %%R in (1,1,%r1%) do call set /a n%%R=%%random%%*240/32678+1
echo %r1%
echo %n1% %n2% %n3% %n4% %n5% %n6% %n7% %n8% %n9% %n10%
pause
Here is a simliar post to generate random numbers in a batch file
You need to include the following at the start of your batch file
setlocal EnableDelayedExpansion
Random variable not changing in "for" loop in windows batch file
#ECHO OFF
SETLOCAL
:obno
set /a r1=%RANDOM% %% 10 + 1
:ob
for /L %%R in (1,1,%r1%) do CALL set /a n%%R=%%random%% %%%% 240 + 1
echo %r1%
echo %n1% %n2% %n3% %n4% %n5% %n6% %n7% %n8% %n9% %n10%
GOTO :EOF
Since %random% produces a random number 0..32767, your original code will waste a lot of time waiting for a number between 0 and 10. Also, it may generate 0.
The % operator is used for 'modulus'. Within a batch, you need to double it, so %random% %% 10 produces 0..9
By CALLing a command, you can avoid the need to setlocal enabledelayedexpansion BUT you also need to redouble any % you use, hence the doubling around random and quadrupling for the mod operator.
The setlocal isn't strictly necessary, BUT what it does is back out enviroment changes when the batch ends, so r1,n1..n10 simply no longer exist in the environment once the batch finishes. If you OMIT the setlocal then any variable set in one run will REMAIN set for a subsequent run, so if your first run sets n5 (and also therefore n1..n4) then if the second run sets r1=2, n3..n5 would be displayed with the stale data from the previous run.
Finally, if you allow r1=0 then since none of n1..n10 are set, the echo %n1%... command will be resolved to echo, will will show the echo status (Echo is on/off). This can be overcome by using the syntax echo(%n1%.... The character directly following the O has been found to do some strange things.
echoDot for instance is traditional for a blak newline. The ( may look odd and appear to 'unbalance' the nesting, but works quite happily to produce a newline with empty arguments.
Let's say that I have a batch file that reads arbitrary integers from a file. The file is structured such that each line contains one integer, like so:
24
17
43
103
...
I need to calculate the average of the top 20 numbers that are in the file. In order to do that, I need some sort of data structure that stores the top 20 numbers. However, as far as I know there are no arrays in batch files. I may have to resort to using temporary files or some other method that I am not aware of. So my ultimate goal is to determine the best approach for implementing some sort of sorting algorithm for the batch file and calculate the average of the top 20 integers.
There is a constraint that I need to place on the problem. The file is pretty huge in terms of size (around ~500 lines) so I would rather not use temporary files due to the huge amount of read/write operations done (unless if you can convince me otherwise of course).
You can mimic arrays in batch. Take a look at Using Arrays in Batch Files.
The following solution uses arrays as described in the article by the link provided in the #Stoney's answer. It also uses zero-padding for correct sorting, which is a nice idea by #jeb, although this solution doesn't use the sort command. Instead, the sorting is automatically done by the SET command, whose output is used for iterating over the 'array'.
#ECHO OFF
SET top=5
SET cnt=0
FOR /F %%N IN (datafile) DO CALL :insert %%N
IF %cnt%==0 GOTO :EOF
IF %cnt% LSS %top% (SET threshold=0) ELSE SET /A threshold=cnt-top
SET s=0
SET i=0
FOR /F "tokens=2 delims=.=" %%A IN ('SET __number.') DO CALL :calc %%A
SET /A res=s/(cnt-threshold)-1000000
ECHO Average is %res%
PAUSE
GOTO :EOF
:insert
SET /A n=1000000+%1
SET /A __number.%n%+=1
SET /A cnt+=1
GOTO :EOF
:calc
SET /A i_prev=i
SET /A i+=__number.%1
IF %i% LEQ %threshold% GOTO :EOF
IF %i_prev% GEQ %threshold% (
SET /A s+=%1*__number.%1
) ELSE (
SET /A "s+=%1*(i_prev+__number.%1-threshold)"
)
Basically, the solution implements the following algorithm:
Pick numbers from the file one by one:
1.1. If the number is encountered for the first time, add it to the array with the count of 1.
1.2. If the number is a duplicate of an already added number, increase the corresponding count value by 1.
1.3. Increase the total count of numbers by 1.
Calculate the threshold value, which is the total count minus the top quantity of numbers whose average is to be calculated.
Iterate through the array items like this:
3.1. Increase the index by the current number's count value.
3.2. If the index exceeds the threshold:
If it has exceeded the threshold at the current iteration, increase the total sum by the product of the number and that part of its count that has exceeded the threshold.
If the threshold was exceeded earlier, increase the total sum by the product of the number and its count.
3.3. If the index doesn't exceed the threshold, omit the item.
Calculate the average as the total sum divided by the difference between total count and threshold.
You can use the sort command to sort the numbers, but there is the porblem, that sort uses a string sort and does not sort numbers, so a 2 seems to be greater than 10.
But this can be solved if you format all numbers to the same length into a temporary file.
So you get
024
017
043
103
...
Sort them with the /R (Reverse) option, to begin the output with the biggest number.
Then you can simply read 20 lines and build the sum for the average
for small files:
#Echo oFF
for /f %%a in (f1.txt) do Call :Append %%a
call :sort %sort%
pause
goto :EOF
:Append
call set sort=%%sort%% %*
goto :eof
:Sort
Setlocal EnableDelayedExpansion
Set/A n=1,s=0,c=s,r=s
for %%: In (%*) do (
Set /a c+=1
Set "nm.!c!=%%:")
:LP.1
if %s% EQU %c% Set/A n+=1,s=0
Set/A s+=1
Call :SPL %n% %s%
If %n% LEQ %c% goto :LP.1
:LP.2
Echo:!nm.%c%!
Set/A c-=1
If %c% GTR 0 goto :LP.2
Endlocal & goto :EOF
:SPL
If !nm.%1! GTR !nm.%2! (
Set "t=!nm.%2!"&Set "nm.%2=!nm.%1!"
Set "nm.%1=!t!"
)
goto :EOF
or a variant:
#echo off
for /f %%# in (f1.txt) do (
set x=##########%%#
call set #%%x:~-0xa%%==)
for /f "delims=#=" %%a in ('set ##') do echo(%%a
pause
there are also: gnu sort