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

Problem 7


この問題は、10001番目の素数を求めるというのがミソです。だからエラトステネスのふるいをいくつまですればいいのかわかりません。ここは手抜きをします。N = 10001として、N log2 Nまでふるいにかけることにします。ちょっと大きめですが、これでOKとします。素数定理に出てくる自然対数ではなく2を底にしているのは計算が簡単だからです。

:log2
    setlocal
    if %1% == 1 exit /b 0
    set /a n = "%1 >> 1"
    call :log2 %n%
    set /a m = %ERRORLEVEL% + 1
    exit /b %m%

ビット演算は上のように引用符でくくります。それは記号が他の用途とかぶるからでしょう。
これでN番目の求めることができます。
しかし、下のコードはなぜか遅くて帰ってきませんでした。

@echo off
echo %TIME%

set /a N = 10001
call :get_time
set /a t = %ERRORLEVEL%
call :log2 %N%
set /a M = %N% * %ERRORLEVEL%
set /a a.size = %M% + 1
call :fill_vector a 1
call :sieve a %M%
call :n_th a %N%
echo %ERRORLEVEL%
call :get_time
set /a t = %ERRORLEVEL% - %t%
echo %t:~0,-2%.%t:~-2%s
exit /b 0

:sieve
    set sieve_v=%1
    set /a max_n = %2
    set /a sieve_k = 1
    :loop_sieve
        set /a sieve_k += 1
        set /a sq_sieve_k = %sieve_k% * %sieve_k%
        if %sq_sieve_k% GTR %max_n% exit /b 0
        set /a is_prime = "a_%sieve_k%"
        if %is_prime% == 0 goto :loop_sieve
        set /a twice_k = %sieve_k% * 2
        for /L %%i in (%twice_k%, %sieve_k%, %max_n%) do (
            set /a a_%%i = 0
        )
        goto :loop_sieve

:n_th
    setlocal
    set /a counter = 0
    set /a k = 2
    :loop_n_th
        set /a e = "%1_%k%"
        if %e% == 1 set /a counter += 1
        if %counter% == %2 exit /b %k%
        set /a k += 1
        goto :loop_n_th

:log2
    setlocal
    if %1% == 1 exit /b 0
    set /a n = "%1 >> 1"
    call :log2 %n%
    set /a m = %ERRORLEVEL% + 1
    exit /b %m%

:fill_vector
    set /a max_index = "%1.size" - 1
    for /L %%i in (0, 1, %max_index%) do set /a %1_%%i = %2
    exit /b 0

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

:get_time
    setlocal
    set t=%TIME%
    set /a h = 1%t:~0,2% %% 100
    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%