批处理 - 查找排列

问题描述:

我是批处理新手,我想知道是否可以按顺序查找所有数字组合。批处理 - 查找排列

在这种情况下,我有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 

但是它返回:

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的出现。

我似乎无法解决它,请帮助,非常感谢!

+0

“关闭”问题的方法是给出答案的反馈并选择一个作为答案;这样做会将问题标记为“回答”,并为OP和回答者提供信誉点。在问题标题中添加“CLOSED”或任何其他类似的横幅是本网站不使用的做法。 – Aacini

当你发布一个问题时,你应该发布你的努力来解决它,描述你使用的方法和问题;否则,你可能会得到根本不需要做任何的解释类似的答案,像这样的:

编辑:随着用户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

2ND EDIT:添加更快的方法

当要解决的问题是过程时间过长t akes,一个明显的选择是使用更快的编程语言。下面使用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); 
       } 
      } 
     } 
     } 
    } 
} 

这是一个批次的JScript混合脚本;用.BAT扩展名保存。这个程序在我便宜又慢的笔记本电脑上用不到9分钟的时间生成了一个有13983816行的239 MB文件。

+0

您的算法存在严重问题。正确的结果应该有13,983,816行,而不是4,774。我注意到你的输出只有39行,其中第二个值是3,当用参数49和6调用时。 – dbenham

+0

据我所知,修复应该很简单:将标签':nextSet'向上移动一行在行'set/A i = M-1'之前。 – aschipfl

+0

为了提高性能,我打算提出以下更改:1.在'if%C%'块之后移动行'set“C =!C [%i%]!”'; 2.在标签':nextPos'后插入'set/A C = C [%i%] + i-M'; 3.用'if%C%gtr%N'替换'if%C%equ%N%'; 4.删除行'if!C [%M%]! gtr%N%';然而,虽然这些修改避免了额外的'goto'循环和多个'for/L %% i'循环,他们使我的电脑上的代码变得更加慢65%左右(将输出重定向到“nul”并使用参数'20'和'5')。你知道这是为什么吗? 'set/A'是一个如此缓慢的野兽? – aschipfl

未来,请展示一些代码,证明您试图自己解决问题,显示卡住的位置,输出的位置与预期不符,等等。问题类似“这是我的要求。对我而言“在这里一般不受欢迎。如果你没有显示任何代码,你得到一个upvote是超越我,但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分钟。

+0

我刚刚编写了这个嵌套的for/L'循环解决方案,但是无法通过该死的电话发布。当我注释掉'echo'语句时,它会在5分钟左右完成,fwiw。无论如何,+1。 – rojo

+0

@rojo我尝试了两种方法并找到了两种作品,但dbenham的作品似乎更快。 – SteveFest