大きな配列を取ると劇的に遅くなるので、小さい配列でエラトステネスのふるい的に約数の和を取ってみました。ただし、そうすると約数の和のためにもう一つ配列が必要になります。例えば、10なら、a[10]=10とb[10]=1とします。bが約数の和です。2で割り切れるから、a[10]=10/2=5、b[10]=1*(1+2)=3、5が残っているので、b[10]=3*(1+5)=18。真の約数の和は8となります。そして、過剰数だったら最初にやった2から割っていく方法を使います。その際のsqrtは前回の方法を使います。
最初の方法より少し速くなりました。やはりsqrtがネックのようです。
@echo off setlocal enabledelayedexpansion set /a N = 10000 call :num_sqrt_iteration %N% set /a num_iter = %ERRORLEVEL% + 1 set /a N_1 = %N% + 1 call :sqrt %N% set /a M = %ERRORLEVEL% rem // calculate prime numbers set /a ps.size = 1 set /a ps_0 = 2 for /L %%k in (3, 2, %M%) do ( call :is_prime %%k if !ERRORLEVEL! == 1 call :push_vector ps %%k ) rem // calculate sum of divisors set /a s = 0 for /L %%b in (2, %M%, %N_1%) do ( set /a e = %%b + %M% call :sieve a b %%b !e! set /a e_1 = !e! - 1 for /L %%n in (%%b, 1, !e_1!) do ( set /a k = %%n - %%b set /a sd = "b_!k!" - %%n if !sd! GTR %%n ( call :sum_divs !sd! if %%n == !ERRORLEVEL! set /a s += !sd! + %%n ) ) ) echo %s% exit /b 0 :num_sqrt_iteration setlocal set /a r = %1 set /a counter = 0 :loop_sqrt set /a r1 = (%r% + %1 / %r%) / 2 set /a counter += 1 if %r1% GEQ %r% exit /b %counter% set /a r = %r1% goto :loop_sqrt :sqrt setlocal set /a r = %1 for /L %%k in (1, 1, %num_iter%) do ( set /a r1 = !r! + %1 / !r! set /a r1 /= 2 if !r1! GEQ !r! exit /b !r! set /a r = !r1! ) :is_prime set /a max_index = "ps.size" - 1 for /L %%k in (0, 1, %max_index%) do ( set /a r = %1 %% "ps_%%k" if !r! == 0 exit /b 0 ) exit /b 1 :sieve set /a _e = %4 - 1 call :range %1 %3 %4 call :fill %2 %M% 1 set /a k = 0 set /a p = 2 :loop_sieve set /a _b = (%3 + %p% - 1) / %p% * %p% if %_b% == %p% set /a _b += %p% for /L %%k in (%_b%, %p%, %_e%) do ( set /a i = %%k - %3 set /a a_!i! /= %p% set /a _s = 1 + %p% set /a r = "a_!i!" %% %p% if !r! == 0 call :div_exp set /a b_!i! *= !_s! ) set /a k += 1 set /a p = "ps_%k%" if %p% GTR 0 ( set /a p_sq = %p% * %p% if !p_sq! LSS %4 goto :loop_sieve ) set /a M_1 = %M% - 1 for /L %%k in (0, 1, %M_1%) do ( set /a ak = "%1_%%k" if !ak! GTR 1 set /a %2_%%k *= !ak! + 1 ) exit /b 0 :div_exp :loop_div_exp set /a a_!i! /= %p% set /a _s = !_s! * %p% + 1 set /a r = a_!i! %% %p% if %r% NEQ 0 exit /b 0 goto :loop_div_exp :sum_divs setlocal set /a s = 1 call :sqrt %1 set /a max = %ERRORLEVEL% for /L %%d in (2, 1, %max%) do ( set /a r = %1 %% %%d if !r! == 0 ( set /a s += %%d set /a q = %1 / %%d if %%d NEQ !q! set /a s += !q! ) ) exit /b %s% :range if "%3" == "" call :range %1 0 %2 1 & exit /b 0 if "%4" == "" call :range %1 %2 %3 1 & exit /b 0 set /a max_index = (%3 - %2) / %4 - 1 set /a %1.size = %max_index% + 1 for /L %%i in (0, 1, %max_index%) do ( set /a %1_%%i = %2 + %%i * %4 ) exit /b 0 :fill set /a %1.size = %2 set /a max_index = %2 - 1 for /L %%k in (0, 1, %max_index%) do set /a %1_%%k = %3 exit /b 0 :push_vector set /a size = "%1.size" set /a %1_%size% = %2 set /a %1.size += 1 exit /b 0