如何使用批处理脚本查找大的阶乘

5
@echo off
if %1.==. ( echo Missing parameter! Try passing the number as a parameter     like   'factorial   10' without the quotes.
goto end )
setlocal enabledelayedexpansion
set /a count=0
set /a temp=0
set /a digits=1
set /a array1=1
for /L %%i IN (2,1,%1) do (
set /a temp=0
for /L %%j IN ( 1,1,!digits! ) do (
set /a temp=!temp!+!array%%j!*%%i
set /a array%%j=!temp!%%10
set /a temp=!temp!/10   ) 

set /a index=!digits!+1

for /L %%v IN (!index!,1,!index! ) do (
if !temp! NEQ 0 (
set /a array!index!=!temp!%%10
set /a temp/=10
set /a index+=1 ))
set /a digits=!index!-1)
for /l %%v IN ( !digits!,-1,1 ) do set array=!array!!array%%v!
echo !array!
echo Total # of decimal digits = !digits!
:end
pause

这是我目前得到的结果。在10以下很稳定,但一旦达到15或20,它就会缺少一些数字。
我的编辑...
@echo off
if %1.==. ( echo Missing parameter! Try passing the number as a parameter  like   'factorial   10' without the quotes.
goto end )
setlocal enabledelayedexpansion
set /a count=0
set /a temp=0
set /a digits=1
set /a array1=1
for /L %%i IN (2,1,%1) do (
set /a temp=0
for /L %%j IN ( 1,1,!digits! ) do (
set /a temp=!temp!+!array%%j!*%%i
set /a array%%j=!temp!%%10
set /a temp=!temp!/10   ) 

for /l %%v IN ( 1,1,30 ) do (
if !temp! neq 0 (
set /a digits+=1
set /a array!digits!=!temp!%%10
set /a temp=!temp!/10
)))
for /l %%v IN ( !digits!,-1,1 ) do set array=!array!!array%%v!
echo !array!
echo Total # of decimal digits = !digits!
:end
pause

现在我正在强制运行最后一个内部循环,即使在那之前temp就已经为零了。有没有办法编写类似于以下c代码的代码片段: while (temp) {/在此处输入代码/}

这只执行到temp为零。

对于批处理中的变量,是否有特定的大小限制? 我知道批处理中的计算程序很难调试,但我想用我所知道的所有编程语言编写它们,以便比较它们的速度。请问有人可以指出我在这里做错了什么吗?

编辑........22/12/13

@echo off
if %1.==. ( echo Missing parameter! Try passing the number as a parameter like   'factorial   10' without the quotes.
goto end )
setlocal enabledelayedexpansion

set /a count=0
set /a tempo=0
set /a digits=1
set /a array1=1

for /f "tokens=1-4 delims=:.," %%a IN ("%time%") do (     
set /a "start=(((%%a*60)+1%%b %% 100)*60+1%%c %%100)*100+1%%d %% 100"
)

for /L %%i IN (2,1,%1) do (
set /a tempo=0
for /L %%j IN ( 1,1,!digits! ) do (
set /a tempo=!tempo!+!array%%j!*%%i
set /a array%%j=!tempo!%%10000
set /a tempo=!tempo!/10000   ) 

for /l %%v IN (1,1,2) do (
if !tempo! neq 0 (
set /a digits+=1
    set /a array!digits!=tempo%%10000
set /a tempo=tempo/10000
))
)

for /l %%v IN ( !digits!,-1,1 ) do set array=!array!!array%%v!
echo !array!  
echo Total # of decimal digits = !digits!

for /f "tokens=1-4 delims=:.," %%a in ("%time%") do (
set /a "end=(((%%a*60)+1%%b %% 100)*60+1%%c %%100)*100+1%%d %% 100"
)

set /a elapsedcs=end-start
set /a elapseds=elapsedcs/100
set /a elapsedcs=elapsedcs%%100
echo %elapseds%.%elapsedcs% seconds
:end
rem label should never be the last statement

这是您所指的吗,aacini?
3个回答

3

为了在批处理中对大数进行快速算术运算,必须将Bignum拆分为数字组,这些数字可以通过set /A命令的32位操作进行管理。最大的32位有符号整数是2147483647,因此可用此方式相乘的最大数字组长度为4,因为5个数字(99999 x 99999)超过了最大数字。加法和乘法必须从右到左进行,并将“进位”传递到左侧的下一组。减法和除法必须从左到右进行,并将“借位”传递到右侧的下一组。以下批处理文件使用此方法将Bignum连续乘以4位因子,因此可以计算高达9999!只要包含4个数字组的所有变量都符合环境的64 MB大小限制(在“设置环境变量”下查找“65,536 KB最大大小”)。结果直接输出到屏幕上,以避免一个批处理变量的8192位数限制。

编辑:我稍微修改了程序,以便更快地运行并获得结果中数字的数量。

@echo off

if "%1" equ "" (
   echo Missing parameter! Try passing the number as a parameter like 'factorial 10' without the quotes.
   goto end
)

setlocal EnableDelayedExpansion

rem Calculate the factorial
set /A g1=1, groups=1
for /L %%n in (2,1,%1) do (
   set carry=0
   for /L %%g in (1,1,!groups!) do (
      set /A group=g%%g*%%n+carry, g%%g=group%%10000, carry=group/10000
   )
   if !carry! neq 0 (
      set /A groups+=1
      set g!groups!=!carry!
   )
)

rem Show the factorial
set /P "=!g%groups%!" < NUL
set /A groupsM1=groups-1
for /L %%g in (%groupsM1%,-1,1) do (
   set group=000!g%%g!
   set /P "=!group:~-4!" < NUL
)
echo/

rem Get the number of digits
set digits=0
for /L %%i in (0,1,3) do if "!g%groups%:~%%i,1!" neq "" set /A digits+=1
set /A digits+=4*groupsM1
echo Total # of decimal digits = %digits%

:end
pause

兄弟,请看一下我的修改。我真的不明白经过时间特性是如何工作的,我只是从论坛上复制粘贴了它。你能解释一下吗?我知道如何使用分隔符和/f开关来使用for循环,但为什么是1%%b %% 100而不是只有%%b呢? - krish
还有一件事,我是 Stack Overflow 的新手(你可以从我获得的声望了解到)。是否有任何方法可以发布代码而不是复制整个代码并在每行开头缩进 4 个空格。 - krish
@krish: 1. 是为了消除被set /A视为八进制数的左侧零,因此09会引发错误。这样,1%%b将给出109,而1%%b %% 100将给出109除以100的余数=9。2. 首先选择所需代码,然后单击编辑窗口上方放置的花括号{} - Aacini
非常感谢你,我终于开始理解了。只有一个问题,就是你在这个回答中提到的内容。我读了你关于计算时间差的回答,因为你的解释很清晰,所以我现在明白了。但是,在Gynnad的回答中,他使用了类似1%STARTTIME:~0,2%-100)的东西,这是什么? - krish

2

我不得不重新阅读代码以了解它的功能。很好。

你的代码面临三个限制。

1 - 在内部的%%j%%v循环中,buffer用于将当前值乘以%%i,你会遇到Magoo所指出的限制。你不能使用大于2^31的值进行set /a运算。由于array中的值被限制在0-9之间,这意味着这个限制将不允许你计算大于214748364(约)的数字的阶乘。

2 - 环境变量的大小有限制。它不能超过32767个字符。由于你正在将数字连接起来输出到控制台(下一个限制与此相关),这将使你只能计算小于9273(约)的数字的阶乘。

3 - cmd可以处理的行的长度有限制。它是8191个字符。这不会限制你的计算,但你不能使用将数字表示为变量中连接的方法。如果不改变这种方法,这将使你只能计算小于2727(约)的数字的阶乘。


0

批处理仅限于有符号32位整数。

顺便提一下 - 不要使用 temptmp 作为用户变量。该变量被系统设置为指向存储临时文件的目录的指针。


谢谢,伙计,我应该想到的!你觉得你能在我的编辑上做出改进吗? - krish
兄弟,我问的不是 int 的大小,而是环境变量中可以存储多大的字符串。在我的代码中,我已经将所有数组元素连接到一个新的变量数组中了。请看一下。 - krish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接