Batch file 如何在纯批处理脚本中生成无重复的随机数列表?
我想生成一个随机数列表,其中包含预定义数量的项目Batch file 如何在纯批处理脚本中生成无重复的随机数列表?,batch-file,random,Batch File,Random,我想生成一个随机数列表,其中包含预定义数量的项目%RND\u TOTAL%,范围从%RND\u MIN%到%RND\u MAX%,并具有一定的间隔%RND\u INTER%。当然,这可以通过以下代码片段实现: @echo关闭 setlocal EnableExtensions EnableDelayedExpansion rem随机数的总数: 设置/A“RND_总计=8” 随机数的rem范围(最小、最大、间隔): 设置/A“RND_最小值=1,RND_最大值=10,RND_中间值=1” 通过随机
%RND\u TOTAL%
,范围从%RND\u MIN%
到%RND\u MAX%
,并具有一定的间隔%RND\u INTER%
。当然,这可以通过以下代码片段实现:
@echo关闭
setlocal EnableExtensions EnableDelayedExpansion
rem随机数的总数:
设置/A“RND_总计=8”
随机数的rem范围(最小、最大、间隔):
设置/A“RND_最小值=1,RND_最大值=10,RND_中间值=1”
通过随机数的rem循环:
对于/L%%I in(1,1,%RND_总计%)do(
rem计算一个随机数:
设置/A“RND\U数量[%%I]=!随机!%((RND\U最大值-RND\U最小值)/RND\U中间值+1)*RND\U中间值+RND\U最小值”
echo!RND_NUM[%%I]!
)
端部
退出/B
下面是相应输出的一个示例(4
和9
在这里都是apear两次):
但是如何创建这样一个没有任何重复项的列表呢
当然,我可以使用下面的脚本来检查每个项目是否在数组中可用,比如变量
RND_NUM[]
,但是这种方法效率很低,因为嵌套了for/L
循环,特别是当%RND_TOTAL%
由于多次重复计算而接近范围规范所涵盖的可用随机数计数时:
@echo关闭
setlocal EnableExtensions EnableDelayedExpansion
rem随机数的总数,重复标志(`0`表示无重复):
设置/A“RND\U总计=20,标志\U DUP=0”
随机数的rem范围(最小、最大、间隔):
设置/A“RND_最小值=1,RND_最大值=30,RND_中间值=1”
rem通过随机数循环,在子例程中生成它们:
对于/L%%I in(1,1,%RND_总计%)do(
电话:SUB%%I
echo!RND_NUM[%%I]!
)
端部
退出/B
:SUB
rem获取已收集的随机数:
设置/A“RND\U计数=%1-1”
:循环
rem计算一个随机数:
设置/A“RND\U数量[%1]=!随机!%((RND\U最大值-RND\U最小值)/RND\U中间值+1)*RND\U中间值+RND\U最小值”
rem检查前一个集合中是否出现随机数:
如果%FLAG\u DUP%eq 0(
对于/L%%I in(1,1,%RND\u计数%)do(
如果遇到重复,rem重新计算随机数:
if!RND_NUM[%1]!eq!RND_NUM[%%I](
转到:循环
)
)
)
退出/B
这是相关的示例输出(此处无重复项):
我稍微修改了一段时间前编写的代码:
@echo off
setlocal EnableDelayedExpansion
rem total number of random numbers:
set /A "RND_TOTAL=8"
rem range for random numbers (minimum, maximum):
set /A "RND_MIN=1, RND_MAX=10"
set /A "range=RND_MAX-RND_MIN+1"
rem Create an input list with all numbers in given range
set "input="
for /L %%i in (%RND_MIN%,1,%RND_MAX%) do (
set "input=!input! %%i"
)
set "input=%input% "
echo IN: [%input%]
rem Extract RND_TOTAL elements from input list in random order
set "output="
for /L %%i in (%RND_TOTAL%,-1,1) do (
set /A "randIndex=(!random!*range)/32768+1, range-=1"
call :MoveInputToOutput !randIndex!
)
echo OUT: [%output%]
goto :EOF
:MoveInputToOutput randIndex
for /F "tokens=%1" %%n in ("%input%") do (
set output=%output% %%n
set input=!input: %%n = !
)
exit /B
输出示例:
> test
IN: [ 1 2 3 4 5 6 7 8 9 10 ]
OUT: [ 8 7 9 3 1 4 5 2]
> test
IN: [ 1 2 3 4 5 6 7 8 9 10 ]
OUT: [ 9 2 10 6 4 8 5 1]
> test
IN: [ 1 2 3 4 5 6 7 8 9 10 ]
OUT: [ 1 2 4 3 8 5 7 9]
对不起,我不明白什么是%RND\u INTER%
值用于
编辑:使用%RND\u INTER%
值且不限制生成的随机数的新版本
@echo off
setlocal 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 Create an input vector with all numbers in given range
set "n=0"
for /L %%i in (%RND_MIN%,%RND_INTER%,%RND_MAX%) do (
set /A n+=1
set "in[!n!]=%%i"
)
echo Input:
set in[
echo/
rem Extract RND_TOTAL elements from input vector in random order
for /L %%i in (1,1,%RND_TOTAL%) do (
set /A "randIndex=(!random!*n)/32768+1"
set /A "out[%%i]=in[!randIndex!], in[!randIndex!]=in[!n!], n-=1"
)
echo Output:
set out[
替换
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]!
)
与
每次选择时,
rnd_num{selectionmade}
被设置为Y
,因此如果再次选择,它将被定义并跳过记录/输出/计数-1-less。Aacini的原始解决方案是有效的,但是,它最多只能支持31个可能的值,因为FOR/F不能读取超过31个标记编辑-他的第二个解决方案消除了这一限制
下面是一个类似的概念,它对列表中的数字使用恒定宽度。这使我能够轻松地使用子字符串操作来提取随机选择的值,并从列表中删除每个值
如前所述,此解决方案支持从0到9999的值,最大可能值数=0
::RND_MAX虽然你们发布了非常好的解决方案,但我不忍心放弃,因此我不得不自己尝试,在问题中找到更好的代码;我们走吧 基本思想是生成一个由两列组成的表,其中第一列包含重复的随机数,第二列包含构成所有可能随机数的索引。下一步是按第一列对该表进行排序。最后,您需要从第二列中根据需要从任意多行中选取值(在我的方法中,我使用最后一行)。所有检索到的数字都是唯一的,因为第二列不包含任何重复项。这里是一个例子,假设我们需要
1,2,3,4,5,6,7,8,9,10
中的8
随机数:
如您所见,没有返回重复的数字。这种方法的最大优点是,如果已经使用了相同的随机数,则不必重复计算随机数。缺点是需要创建完整的表,即使在可能的大量数字中只需要几个唯一的随机数
我不得不承认,这项技术不是我的主意,但不幸的是,我不知道它归功于谁
以下是我的代码,其中包含一些解释性说明:
@echo关闭
setlocal EnableExtensions EnableDelayedExpansion
rem随机数的总数,重复标志(`0`表示无重复):
设置/A“RND\U总计=8,标志\U DUP=0”
随机数的rem范围(最小、最大、间隔):
设置/A“RND_最小值=1,RND_最大值=10,RND_中间值=1”
rem将类似表格的数据写入临时文件:
>“%~n0.tmp”(
rem循环通过所有可能的随机数:
对于/L%%I in(%RND\U最小值%、%RND\U中间值%、%RND\U最大值%)执行(
rem计算一个随机数:
设置/A“随机数=!随机数!%((随机数最大值-随机数最小值)/随机数中间值+1)*随机数中间值+随机数最小值”
如果%FLAG\u DUP%eq 0(
rem重复被拒绝,因此以随机数作为第一列构建行:
echo!RND_NUM!,%%I
)否则(
允许rem重复,因此使用循环计数器作为第一列生成行:
回声%%I,!RND_NUM!
)
)
)
rem确定需要跳过表中的多少项才能获得总数:
设置/A“跳过=(RND\U最大值-RND\U最小值)/RND\U中间值+1,跳过-=RND\U总计”
如果%跳过%GTR 0(
@echo off
setlocal 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 Create an input vector with all numbers in given range
set "n=0"
for /L %%i in (%RND_MIN%,%RND_INTER%,%RND_MAX%) do (
set /A n+=1
set "in[!n!]=%%i"
)
echo Input:
set in[
echo/
rem Extract RND_TOTAL elements from input vector in random order
for /L %%i in (1,1,%RND_TOTAL%) do (
set /A "randIndex=(!random!*n)/32768+1"
set /A "out[%%i]=in[!randIndex!], in[!randIndex!]=in[!n!], n-=1"
)
echo Output:
set out[
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]!
)
for /L %%I in (%rnd_min%,1,%RND_max%) do set "rnd_num{%%I}="
set /a rnd_count=rnd_total
:rnd_loop
rem compute a random number:
set /A "RND_selection=%RANDOM%%%((RND_MAX-RND_MIN)/RND_INTER+1)*RND_INTER+RND_MIN"
if not defined rnd_num{%rnd_selection%} (
SET "rnd_num{%rnd_selection%}=Y"
set /a rnd_num[%count%]=rnd_selection
echo %rnd_selection%
set /a rnd_count-=1
)
if %rnd_count% neq 0 goto rnd_loop
@echo off
setlocal enableDelayedExpansion
:: This script has the following limitations:
:: RND_MIN >= 0
:: RND_MAX <= 9999
:: ((RND_MAX - RND_MIN + 1) / RND_INTER) <= 1354
::
set "RND_MIN=1"
set "RND_MAX=10"
set "RND_INTER=1"
set "RND_TOTAL=8"
set /a cnt=(RND_MAX - RND_MIN + 1) / RND_INTER
:: Define a string containing a space delimited list of all possible values,
:: with each value having 10000 added
set "pool="
set /a "beg=RND_MIN+10000, end=RND_MAX+10000, cnt=(RND_MAX-RND_MIN+1)/RND_INTER"
for /l %%N in (%beg% %RND_INTER% %end%) do set "pool=!pool!%%N "
:: Build the randomly sequenced array of numbers
for /l %%N in (1 1 %RND_TOTAL%) do (
%= Randomly select a value from the pool of all possible values =%
%= and compute the index within the string, as well as the index =%
%= of the next value =%
set /a "loc=(!random!%%cnt)*6, next=loc+6"
%= Transfer the index values to FOR variables =%
for %%A in (!loc!) do for %%B in (!next!) do (
%= Assign the selected value to the output array =%
set /a "RND_NUM[%%N]=!pool:~%%A,5!-10000"
%= Remove the value from the pool =%
set "pool=!pool:~0,%%A!!pool:~%%B!"
set /a cnt-=1
)
)
:: Display the results
for /l %%N in (1 1 %RND_TOTAL%) do echo !RND_NUM[%%N]!
generated sorted returned
table table numbers
--------------------------------
1,1 1,1 -
9,2 1,6 -
8,3 10,8 8
3,4 3,4 4
9,5 4,7 7
1,6 5,9 9
4,7 8,10 10
10,8 8,3 3
5,9 9,2 2
8,10 9,5 5
generated sorted returned
2D-array 2D-array numbers
------------------------------------------------
RND_NUM[1,1] RND_NUM[1,1] -
RND_NUM[9,2] RND_NUM[1,6] -
RND_NUM[8,3] RND_NUM[10,8] 8
RND_NUM[3,4] RND_NUM[3,4] 4
RND_NUM[9,5] RND_NUM[4,7] 7
RND_NUM[1,6] RND_NUM[5,9] 9
RND_NUM[4,7] RND_NUM[8,10] 10
RND_NUM[10,8] RND_NUM[8,3] 3
RND_NUM[5,9] RND_NUM[9,2] 2
RND_NUM[8,10] RND_NUM[9,5] 5
@echo off
setlocal
setlocal enabledelayedexpansion
:: Populate array with all target numbers
for /L %%I in (%rnd_min%,1,%RND_max%) do (
set /a idx="%%I"
set "array[!idx!]=%%I
)
set /A I_NEED_SO_MANY_RANDOM_VALUES=%RND_max%-%rnd_min%
set /A TOTAL_UNUSED==%RND_max%-%rnd_min%
for /L %%I in (0,1,%I_NEED_SO_MANY_RANDOM_VALUES%) do (
SET /A next_pick_index=!RANDOM! %% !TOTAL_UNUSED!
SET /A TOTAL_UNUSED-=1
for /f "tokens=* delims= " %%a in ('echo !TOTAL_UNUSED!') do set unused_random=!array[%%a]!
::pick random value from initial array
for /f "tokens=* delims= " %%a in ('echo !next_pick_index!') do set next_random=!array[%%a]!
:: swap picked random value with the last not-used element in array
SET "array[!next_pick_index!]=!unused_random!"
:: put picked random value into new array
SET "randomized_array[%%I]=!next_random!"
)
::output array to console
SET randomized_array[