批处理 - 找出排列
Batch - Find Out Permutation
我是 Batch 的新手,我想知道我是否可以按顺序找出所有数字组合。
在这种情况下,我有 49 个数字,从 1 到 49,我必须选择 6 个数字作为结果。
例如:
1 2 3 4 5 6
1 2 3 4 5 7
...
1 2 3 4 5 49
1 2 3 4 6 7
1 2 3 4 6 8
等...
这是我的旧代码:
@echo off > NEWFILE & setLocal EnableDelayedExpansion
set a=44
set b=45
set c=46
set d=47
set e=48
set f=49
for /L %%a in (1 1 !a!) do (
for /L %%b in (2 1 !b!) do (
for /L %%c in (3 1 !c!) do (
for /L %%d in (4 1 !d!) do (
for /L %%e in (5 1 !e!) do (
for /L %%f in (6 1 !f!) do (
echo.%%a %%b %%c %%d %%e %%f
))))))) >> NEWFILE
goto :EOF
然而 returns:
1 2 3 4 5 6
1 2 3 4 5 7
...
1 2 3 4 5 49
1 2 3 4 6 6
出现了两个 6。
我好像无法修复它,请帮忙,非常感谢!
以后,请展示一些代码,证明您已尝试自己解决问题,显示卡住的地方,输出不符合预期的地方等。类似 "Here are my requirements. Code this for me" 的问题通常是在这里很受欢迎。你是如何在不显示任何代码的情况下获得赞成票的,这超出了我的理解,但是 c'est la vie.
在这种情况下,我发现这个问题很有趣,所以我想我会继续让你开始。已接受的挑战。这是一种方法。
@echo off
setlocal enabledelayedexpansion
set "series=1 2 3 4 5 6"
:loop
echo %series%
if "%series%"=="44 45 46 47 48 49" goto :EOF
for /f "tokens=1-6" %%a in ("%series%") do (
set /a i1=%%a, i2=%%b, i3=%%c, i4=%%d, i5=%%e, i6=%%f+1
if !i6! gtr 49 set /a i5+=1, i6=i5+1
if !i5! gtr 48 set /a i4+=1, i5=i4+1, i6=i5+1
if !i4! gtr 47 set /a i3+=1, i4=i3+1, i5=i4+1, i6=i5+1
if !i3! gtr 46 set /a i2+=1, i3=i2+1, i4=i3+1, i5=i4+1, i6=i5+1
if !i2! gtr 45 set /a i1+=1, i2=i1+1, i3=i2+1, i4=i3+1, i5=i4+1, i6=i5+1
set "series=!i1! !i2! !i3! !i4! !i5! !i6!"
)
goto loop
这是另一个应该更有效的解决方案。
@echo off
setlocal enabledelayedexpansion
set "series=1 2 3 4 5 6"
set total=0
for /L %%a in (1,1,44) do (
set /a i2 = %%a + 1
for /L %%b in (!i2!, 1, 45) do (
set /a i3 = %%b + 1
for /L %%c in (!i3!, 1, 46) do (
set /a i4 = %%c + 1
for /L %%d in (!i4!, 1, 47) do (
set /a i5 = %%d + 1
for /L %%e in (!i5!, 1, 48) do (
set /a i6 = %%e + 1
for /L %%f in (!i6!, 1, 49) do (
rem // Uncomment this echo to watch the progress (severely decreases efficiency)
rem echo %%a %%b %%c %%d %%e %%f
set /a total += 1
)
)
)
)
)
echo Total so far: !total!
)
rem // Should have gone through 13983816 iterations
鉴于有 13,983,816 个唯一排列,此问题属于计算密集型问题。 (参见 https://en.wikipedia.org/wiki/Lottery_mathematics#Calculation_explained_in_choosing_6_from_49。)
Rojo 答案应该有效,但 GOTO 和重复的 FOR /F 解析和 IF 逻辑会大大降低速度。
如果使用嵌套的 FOR /L 循环,代码会更快。
@echo off
setlocal enableDelayedExpansion
for /l %%A in (1 1 44) do (
set /a start=%%A+1
for /l %%B in (!start! 1 45) do (
set /a start=%%B+1
for /l %%C in (!start! 1 46) do (
set /a start=%%C+1
for /l %%D in (!start! 1 47) do (
set /a start=%%D+1
for /l %%E in (!start! 1 48) do (
set /a start=%%E+1
for /l %%F in (!start! 1 49) do (
echo %%A %%B %%C %%D %%E %%F
)
)
)
)
)
)
让这个脚本将结果打印到屏幕上仍然会慢得无法忍受。我估计在我的机器上需要 1.25 小时。将输出重定向到文件大约快 5 倍,大约 15 分钟。
当你post一个问题时你应该post你努力解决它,描述使用的方法和你遇到的问题;否则你可能会得到类似的答案而根本没有任何解释,就像这个:
编辑:正如用户 dbenham 和 aschipfl 指出的那样,我的原始代码有一个小错误:set /A i=M-1
行应该放在 之后 :nextSet
标签。这是正确的代码:
@echo off
setlocal EnableDelayedExpansion
set "N=%1"
set "M=%2"
set "line="
for /L %%i in (1,1,%M%) do (
set "C[%%i]=%%i"
set "line=!line! ^!C[%%i]^!"
)
:nextSet
set /A i=M-1
for /L %%j in (!C[%M%]!,1,%N%) do (
set "C[%M%]=%%j"
echo %line%
)
:nextPos
set "C=!C[%i%]!"
if %C% equ %N% (
set /A i-=1
if !i! equ 0 goto :EOF
goto nextPos
)
for /L %%i in (%i%,1,%M%) do (
set /A C+=1,C[%%i]=C
)
if !C[%M%]! gtr %N% goto nextPos
goto nextSet
显然,更正后的代码生成了更多的结果,而且这个版本特别慢...:(
下面的新版本使用了与dbenham解决方案完全相同的代码;它唯一的优点是您可以非常简单地更改用于生成结果的参数:
@echo off
setlocal EnableDelayedExpansion
set "N=%1"
set "M=%2"
set /A j=N-M, prev=0
set "for=" & set "line=" & set "endfor="
for /L %%i in (1,1,%M%) do (
set /A j+=1
set "for=!for! set /A start=!prev!+1 & for /L %%%%i in (^!start^!,1,!j!) do ("
set "line=!line! %%%%i"
set "endfor=!endfor!)"
set "prev=%%%%i"
)
REM ECHO !FOR! echo !LINE! %ENDFOR%
%for% echo %line% %endfor%
输出示例:
C:\> test.bat 6 4
1 2 3 4
1 2 3 5
1 2 3 6
1 2 4 5
1 2 4 6
1 2 5 6
1 3 4 5
1 3 4 6
1 3 5 6
1 4 5 6
2 3 4 5
2 3 4 6
2 3 5 6
2 4 5 6
3 4 5 6
要获得结果,请使用:test.bat 49 6
第二次编辑:添加了更快的方法
当要解决的问题是进程花费的时间过多时,一个明显的替代方法是使用更快的编程语言。下面的解决方案使用JScript,有点类似于批处理文件编程:
@if (@CodeSection == @Batch) @then
@echo off
echo Start: %time%
cscript //nologo //E:JScript "%~F0" > result.txt
echo End: %time%
goto :EOF
@end
// JScript code section
for ( var A=1; A <= 44; ++A ) {
for ( var B=A+1; B <= 45; ++B ) {
for ( var C=B+1; C <= 46; ++C ) {
for ( var D=C+1; D <= 47; ++D ) {
for ( var E=D+1; E <= 48; ++E ) {
for ( var F=E+1; F <= 49; ++F ) {
WScript.Echo(A,B,C,D,E,F);
}
}
}
}
}
}
这是一个 Batch-JScript 混合脚本;使用 .BAT 扩展名保存它。这个程序在我便宜又慢的笔记本电脑上用了不到 9 分钟就生成了一个 239 MB 的文件,其中包含 13983816 行。
我是 Batch 的新手,我想知道我是否可以按顺序找出所有数字组合。
在这种情况下,我有 49 个数字,从 1 到 49,我必须选择 6 个数字作为结果。
例如:
1 2 3 4 5 6
1 2 3 4 5 7
...
1 2 3 4 5 49
1 2 3 4 6 7
1 2 3 4 6 8
等...
这是我的旧代码:
@echo off > NEWFILE & setLocal EnableDelayedExpansion
set a=44
set b=45
set c=46
set d=47
set e=48
set f=49
for /L %%a in (1 1 !a!) do (
for /L %%b in (2 1 !b!) do (
for /L %%c in (3 1 !c!) do (
for /L %%d in (4 1 !d!) do (
for /L %%e in (5 1 !e!) do (
for /L %%f in (6 1 !f!) do (
echo.%%a %%b %%c %%d %%e %%f
))))))) >> NEWFILE
goto :EOF
然而 returns:
1 2 3 4 5 6
1 2 3 4 5 7
...
1 2 3 4 5 49
1 2 3 4 6 6
出现了两个 6。
我好像无法修复它,请帮忙,非常感谢!
以后,请展示一些代码,证明您已尝试自己解决问题,显示卡住的地方,输出不符合预期的地方等。类似 "Here are my requirements. Code this for me" 的问题通常是在这里很受欢迎。你是如何在不显示任何代码的情况下获得赞成票的,这超出了我的理解,但是 c'est la vie.
在这种情况下,我发现这个问题很有趣,所以我想我会继续让你开始。已接受的挑战。这是一种方法。
@echo off
setlocal enabledelayedexpansion
set "series=1 2 3 4 5 6"
:loop
echo %series%
if "%series%"=="44 45 46 47 48 49" goto :EOF
for /f "tokens=1-6" %%a in ("%series%") do (
set /a i1=%%a, i2=%%b, i3=%%c, i4=%%d, i5=%%e, i6=%%f+1
if !i6! gtr 49 set /a i5+=1, i6=i5+1
if !i5! gtr 48 set /a i4+=1, i5=i4+1, i6=i5+1
if !i4! gtr 47 set /a i3+=1, i4=i3+1, i5=i4+1, i6=i5+1
if !i3! gtr 46 set /a i2+=1, i3=i2+1, i4=i3+1, i5=i4+1, i6=i5+1
if !i2! gtr 45 set /a i1+=1, i2=i1+1, i3=i2+1, i4=i3+1, i5=i4+1, i6=i5+1
set "series=!i1! !i2! !i3! !i4! !i5! !i6!"
)
goto loop
这是另一个应该更有效的解决方案。
@echo off
setlocal enabledelayedexpansion
set "series=1 2 3 4 5 6"
set total=0
for /L %%a in (1,1,44) do (
set /a i2 = %%a + 1
for /L %%b in (!i2!, 1, 45) do (
set /a i3 = %%b + 1
for /L %%c in (!i3!, 1, 46) do (
set /a i4 = %%c + 1
for /L %%d in (!i4!, 1, 47) do (
set /a i5 = %%d + 1
for /L %%e in (!i5!, 1, 48) do (
set /a i6 = %%e + 1
for /L %%f in (!i6!, 1, 49) do (
rem // Uncomment this echo to watch the progress (severely decreases efficiency)
rem echo %%a %%b %%c %%d %%e %%f
set /a total += 1
)
)
)
)
)
echo Total so far: !total!
)
rem // Should have gone through 13983816 iterations
鉴于有 13,983,816 个唯一排列,此问题属于计算密集型问题。 (参见 https://en.wikipedia.org/wiki/Lottery_mathematics#Calculation_explained_in_choosing_6_from_49。)
Rojo 答案应该有效,但 GOTO 和重复的 FOR /F 解析和 IF 逻辑会大大降低速度。
如果使用嵌套的 FOR /L 循环,代码会更快。
@echo off
setlocal enableDelayedExpansion
for /l %%A in (1 1 44) do (
set /a start=%%A+1
for /l %%B in (!start! 1 45) do (
set /a start=%%B+1
for /l %%C in (!start! 1 46) do (
set /a start=%%C+1
for /l %%D in (!start! 1 47) do (
set /a start=%%D+1
for /l %%E in (!start! 1 48) do (
set /a start=%%E+1
for /l %%F in (!start! 1 49) do (
echo %%A %%B %%C %%D %%E %%F
)
)
)
)
)
)
让这个脚本将结果打印到屏幕上仍然会慢得无法忍受。我估计在我的机器上需要 1.25 小时。将输出重定向到文件大约快 5 倍,大约 15 分钟。
当你post一个问题时你应该post你努力解决它,描述使用的方法和你遇到的问题;否则你可能会得到类似的答案而根本没有任何解释,就像这个:
编辑:正如用户 dbenham 和 aschipfl 指出的那样,我的原始代码有一个小错误:set /A i=M-1
行应该放在 之后 :nextSet
标签。这是正确的代码:
@echo off
setlocal EnableDelayedExpansion
set "N=%1"
set "M=%2"
set "line="
for /L %%i in (1,1,%M%) do (
set "C[%%i]=%%i"
set "line=!line! ^!C[%%i]^!"
)
:nextSet
set /A i=M-1
for /L %%j in (!C[%M%]!,1,%N%) do (
set "C[%M%]=%%j"
echo %line%
)
:nextPos
set "C=!C[%i%]!"
if %C% equ %N% (
set /A i-=1
if !i! equ 0 goto :EOF
goto nextPos
)
for /L %%i in (%i%,1,%M%) do (
set /A C+=1,C[%%i]=C
)
if !C[%M%]! gtr %N% goto nextPos
goto nextSet
显然,更正后的代码生成了更多的结果,而且这个版本特别慢...:(
下面的新版本使用了与dbenham解决方案完全相同的代码;它唯一的优点是您可以非常简单地更改用于生成结果的参数:
@echo off
setlocal EnableDelayedExpansion
set "N=%1"
set "M=%2"
set /A j=N-M, prev=0
set "for=" & set "line=" & set "endfor="
for /L %%i in (1,1,%M%) do (
set /A j+=1
set "for=!for! set /A start=!prev!+1 & for /L %%%%i in (^!start^!,1,!j!) do ("
set "line=!line! %%%%i"
set "endfor=!endfor!)"
set "prev=%%%%i"
)
REM ECHO !FOR! echo !LINE! %ENDFOR%
%for% echo %line% %endfor%
输出示例:
C:\> test.bat 6 4
1 2 3 4
1 2 3 5
1 2 3 6
1 2 4 5
1 2 4 6
1 2 5 6
1 3 4 5
1 3 4 6
1 3 5 6
1 4 5 6
2 3 4 5
2 3 4 6
2 3 5 6
2 4 5 6
3 4 5 6
要获得结果,请使用:test.bat 49 6
第二次编辑:添加了更快的方法
当要解决的问题是进程花费的时间过多时,一个明显的替代方法是使用更快的编程语言。下面的解决方案使用JScript,有点类似于批处理文件编程:
@if (@CodeSection == @Batch) @then
@echo off
echo Start: %time%
cscript //nologo //E:JScript "%~F0" > result.txt
echo End: %time%
goto :EOF
@end
// JScript code section
for ( var A=1; A <= 44; ++A ) {
for ( var B=A+1; B <= 45; ++B ) {
for ( var C=B+1; C <= 46; ++C ) {
for ( var D=C+1; D <= 47; ++D ) {
for ( var E=D+1; E <= 48; ++E ) {
for ( var F=E+1; F <= 49; ++F ) {
WScript.Echo(A,B,C,D,E,F);
}
}
}
}
}
}
这是一个 Batch-JScript 混合脚本;使用 .BAT 扩展名保存它。这个程序在我便宜又慢的笔记本电脑上用了不到 9 分钟就生成了一个 239 MB 的文件,其中包含 13983816 行。