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

powを書いてみましょう。まずバイナリ法をgotoを使って書きます。

@echo off

call :pow 2 1024 3
exit /b 0

:pow
    setlocal
    set /a m = 1
    set /a p = %1
    set /a e = %2
    :loop_pow
        if %e% == 0 exit /b %m%
        set /a r = %e% %% 2
        if !r! == 1 set /a m = %m% * %p% %% %3
        set /a p = %p% * %p% %% %3
        set /a e /= 2
        goto :loop_pow

これをforを使って書きます。eが0になればexitで抜ければいいので、回す回数はべき乗と同じとします。

setlocal enabledelayedexpansion
…

:pow
    setlocal
    set /a m = 1
    set /a p = %1
    set /a e = %2
    for /L %%i in (1, 1, %2) do (
        if !e! == 0 exit /b !m!
        set /a r = !e! %% 2
        if !r! == 1 set /a m = !m! * !p! %% %3
        set /a p = !p! * !p! %% %3
        set /a e /= 2
    )

遅くなっているんですが。こう変えると、

    for /L %%i in (1, 1, 100) do (

あ、速くなりましたね。どうやら指定した回数必ず回すようです。echo onにしてみるとわかります。

@echo on

for /L %%k in (1, 1, 5) do (
    if %%k == 2 exit /b %%k
)

バッチファイルなら指数は32ビット符号付き整数なので32回回せば十分です。

:pow
    setlocal
    set /a m = 1
    set /a p = %1
    set /a e = %2
    for /L %%i in (1, 1, 32) do (
        if !e! == 0 exit /b !m!
        set /a r = !e! %% 2
        if !r! == 1 set /a m = !m! * !p! %% %3
        set /a p = !p! * !p! %% %3
        set /a e /= 2
    )

回す回数を減らす方法もありますが、多くの場合はその必要はありません。例えば1から100万までのべき乗を求めるなら21回回せば十分です。数回で済むこともありますが、多くは20回程度必要になって数回しか必要のない場合はごくわずかです。