バッチファイルでProject Euler(70)

最後に今まで出てこなかったソートをやりたいと思います。
まず、選択ソートはforが使えるので書きやすく、意外と速いです。
それから、クイックソートです。クイックソート再帰で書きますが、setlocalが使えないので変数を退避してくれません。退避しなければならないのはピボットの位置です。自力でスタックを作ろうかと思ったのですが、よく考えると呼び出された再帰関数のほうでその位置を返せばいいことに気がつきました。
以下は、選択ソートとクイックソートの処理速度のベンチマークです。要素数が500以上だとクイックソートが速いという結果になりました。組み合わせると速いんでしょうけどね。

size 選択ソート クイックソート
10 0.05s 0.17s
20 0.12s 0.36s
50 0.40s 1.30s
100 1.26s 3.18s
200 5.10s 8.88s
500 46.46s 28.35s
1000 311.14s 81.41s


今までのコードでバッチファイルの可能性を示せたのではないかと思います。しかし、なかなかデバッグが大変でした。

:sort
    set /a size = "%1.size"
    set /a size_1 = %size% - 1
    set /a size_2 = %size% - 2
    for /L %%i in (0, 1, %size_2%) do (
        set /a min = "%1_%%i"
        set /a i_1 = %%i + 1
        set /a k = %%i
        for /L %%j in (!i_1!, 1, %size_1%) do (
            set /a e = "%1_%%j"
            if !e! LSS !min! (
                set /a min = !e!
                set /a k = %%j
            )
        )
        set /a tmp = "%1_%%i"
        call set /a %1_%%i = %%%1_!k!%%
        set /a %1_!k! = !tmp!
    )
    exit /b 0
@echo off

setlocal enabledelayedexpansion
set /a N = %1
set /a N_1 = %N% - 1
set /a t = 123456
for /L %%k in (0, 1, %N_1%) do (
    set /a r = !t! %% 2
    set /a t /= 2
    if !r! == 1 set /a "t ^= 926252"
    set /a b_%%k = !t!
)
set /a b.size = %N%
call :start_time
call :qsort b
call :check_time
call :check_sort b
echo %ERRORLEVEL%
exit /b 0

:qsort
    set /a size = "%1.size"
    call :qsort_core %1 0 %size%
    exit /b 0

:qsort_core
    set /a size = %3 - %2
    if %size% LEQ 1 exit /b %3
    
    rem // frontより小さい大きいで振り分ける
    set /a front = "%1_%2"
    set /a begin = %2 + 1
    set /a end = %3 - 1
    set /a i = %2
    set /a j = %end%
    for /L %%k in (%begin%, 1, %end%) do (
        set /a e = "%1_%%k"
        if !e! LSS %front% (
            call set /a tmp_!i! = !e!
            set /a i += 1
        ) else (
            call set /a tmp_!j! = !e!
            set /a j -= 1
        )
    )
    set /a tmp_%i% = %front%
    
    for /L %%k in (%2, 1, %3) do (
        set /a %1_%%k = "tmp_%%k"
    )
    call :qsort_core %1 %2 %i%
    set /a end = %ERRORLEVEL% + 1
    call :qsort_core %1 %end% %3
    exit /b %3

:check_sort
    setlocal
    set /a max_index = "%1.size" - 1
    for /L %%k in (1, 1, %max_index%) do (
        set /a l = %%k - 1
        call set /a prev = %%%1_!l!%%
        call set /a next = %%%1_%%k%%
        if !prev! GTR !next! exit /b 0
    )
    exit /b 1

:print_vector
    setlocal
    set /a size = "%1.size"
    if %size% == 0 echo %2 & exit /b 0
    set /a e = "%1_0"
    set s=%e%
    set /a k = 1
    :loop_print_vector
        if %k% == %size% echo %s% & exit /b 0
        set /a e = "%1_%k%"
        set s=%s% %e%
        set /a k += 1
        goto :loop_print_vector

:start_time
    call :get_time
    set /a __t0 = %ERRORLEVEL%
    exit /b 0

:check_time
    setlocal
    call :get_time
    set /a t = %ERRORLEVEL% - %__t0%
    if %t% LSS 10 (
        echo 0.0%t%s
    ) else (
        if %t% LSS 100 (
            echo 0.%t%s
        ) else (
            echo %t:~0,-2%.%t:~-2%s
        )
    )
    exit /b 0

:get_time
    setlocal
    set t=%TIME%
    set /a h = %t:~0,2%
    set /a m = 1%t:~3,2% %% 100
    set /a s = 1%t:~6,2% %% 100
    set /a ss = 1%t:~-2% %% 100
    set /a ret = ((%h% * 60 + %m%) * 60 + %s%) * 100 + %ss%
    exit /b %ret%