[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖

【练习-055】批处理根据输入数求出cmd范围内最大次方数

出题目的:
    1、加深大家对cmd数值处理极限值的认识
    2、考察大家的正、反计算能力
加分原则:
    满分10分,视情形加分以思路为重
解题要求:
    代码简洁、效率高,产生临时文件应该是不存在的事
题目如下:
    要求编写一批处理,实现当用户输入正整数(不考虑负数)时,分别用正算和反算法计算出此数在cmd最大极限内可运算次方数。
示例如下:




[ 本帖最后由 batman 于 2009-12-18 23:26 编辑 ]
附件: 您需要登录才可以下载或查看附件。没有帐号?注册
***共同提高***

正算和反算法分别指什么?
我帮忙写的代码不需要付钱。如果一定要给,请在微信群或QQ群发给大家吧。
【微信公众号、微信群、QQ群】http://bbs.bathome.net/thread-3473-1-1.html
【支持批处理之家,加入VIP会员!】http://bbs.bathome.net/thread-67716-1-1.html

TOP

正算:乘
反算:除
***共同提高***

TOP

回复 1楼 的帖子

  1. @echo off&setlocal enabledelayedexpansion
  2. (set/p=初始化中, 请稍等...<nul)&(call :initRoots)
  3. echo.&echo cmd 下最大正整数 2147483647 (2^^31-1) 的各次方根整数部分(次数2--30):
  4. (for /l %%a in (2,1,30) do set /p=!root%%a!,<nul) & (echo  )
  5. :loop
  6. set/p i=请输入正整数:
  7. if %i% lss 1 goto loop
  8. if %i% equ 1 echo 1的任意次方=1&goto loop
  9. set /a exp=1
  10. for /l %%a in (2,1,30) do if %i% leq !root%%a! set /a exp+=1
  11. echo %i%的%exp%次方接近于cmd最大值.
  12. goto loop
  13. :initRoots 初始化 cmd 下最大正整数 2147483647 (2^31-1) 的各次方根(次数2--30)
  14. for /l %%a in (2,1,30) do (call :searchRoot root%%a 2 50000 %%a)& set /p=^>^><nul
  15. exit /b
  16. :checkOverflow overflow x y rem 检测 x^y 是否溢出
  17. (set tc=%2)&(set te=%3)
  18. :next
  19. if !te! gtr 1 set /a "t0=tc,tc*=%2,t1=tc/ %2,te-=1"
  20. if !t0! neq !t1! (set %1=yes)&(exit /b)
  21. if !t0! geq !tc! (set %1=yes)&(exit /b)
  22. if !tc! leq 0 (set %1=yes)&(exit /b) else if !te! leq 1 (set %1=no)&(exit /b) else goto next
  23. exit /b
  24. :searchRoot root lower upper time
  25. set /a "ll=%2,uu=%3"
  26. :schNext
  27. set /a t=(ll+uu)/2
  28. if !t! equ !ll! set /a "%1=t" & exit /b
  29. call :checkOverflow ofl !t! %4
  30. if /i "!ofl!"=="yes" (set /a "uu=t") else (set /a "ll=t")
  31. goto schNext
  32. exit /b
复制代码

[ 本帖最后由 neorobin 于 2009-12-19 03:42 编辑 ]
1

评分人数

TOP

回复 4楼 的帖子

个人指出存在以下三点问题(好的就不说了):

1、cmd处理的最大值应在代码中计算出来,不能直接给出。

2、题中要求的是分别用正算、反算两种方法,楼上好像就用了正算法。

3、代码写得过于复杂,易读性不好。
***共同提高***

TOP

回复 5楼 的帖子

4 楼代码除了次数 30, 其它并未直接或间接实质性利用 最大正整数 值, 该数仅是 echo 的内容 未起计算作用.
下面代码彻底不利用已知的 最大正整数 值, 反算我就不做了, 呵呵, 至于复杂, 嗯是有些.
  1. @echo off&setlocal enabledelayedexpansion
  2. set t=1
  3. :searchMaxInt
  4. set /a "t<<=1"
  5. if !t! leq 0 ((set /a "t>>=1,maxInt=t-1+t")& echo cmd 下最大正整数为:!maxInt!) else goto searchMaxInt
  6. (set/p=初始化中, 请稍等...<nul)&(call :initRoots)
  7. echo.&echo cmd 下最大正整数 !maxInt! 的各次方根整数部分(整数部分≥2)(次数2--!expMax!):
  8. (for /l %%a in (2,1,!expMax!) do set /p=!root%%a!,<nul) & (echo  )
  9. :loop
  10. set/p i=请输入正整数:
  11. if %i% lss 1 goto loop
  12. if %i% equ 1 echo 1的任意次方=1&goto loop
  13. set /a exp=1
  14. for /l %%a in (2,1,!expMax!) do if %i% leq !root%%a! set /a exp+=1
  15. echo %i%的%exp%次方接近于cmd最大值.
  16. goto loop
  17. :initRoots 初始化 cmd 下最大正整数 !maxInt! 的各次方根(次数2--!expMax!)
  18. for /l %%a in (2,1,256) do (call :searchRoot root%%a 1 !maxInt!/2 %%a)&(if !root%%a! lss 2 set /a expMax=%%a-1& exit /b)& set /p=^>^><nul
  19. exit /b
  20. :checkOverflow overflow x y rem 检测 x^y 是否溢出
  21. (set tc=%2)&(set te=%3)
  22. :next
  23. if !te! gtr 1 set /a "t0=tc,tc*=%2,t1=tc/ %2,te-=1"
  24. if !t0! neq !t1! (set %1=yes)&(exit /b)
  25. if !t0! geq !tc! (set %1=yes)&(exit /b)
  26. if !tc! leq 0 (set %1=yes)&(exit /b) else if !te! leq 1 (set %1=no)&(exit /b) else goto next
  27. exit /b
  28. :searchRoot root lower upper time 二分法搜索方根
  29. set /a "ll=%2,uu=%3"
  30. :schNext
  31. set /a t=(ll+uu)/2
  32. if !t! equ !ll! set /a "%1=t" & exit /b
  33. call :checkOverflow ofl !t! %4
  34. if /i "!ofl!"=="yes" (set /a "uu=t") else (set /a "ll=t")
  35. goto schNext
  36. exit /b
复制代码

[ 本帖最后由 neorobin 于 2009-12-19 23:51 编辑 ]

TOP

回复 1楼 的帖子

请问楼主检测 溢出 有哪些好的方法

TOP

下面这个例子既用了乘法又用了除法
应该是符合“分别用正算和反算法”的要求吧:)
楼上的算法难以理解
最多30次的循环
还用二分搜索之类的玩意嘛?
检测溢出可以用大小比较
但是这个不可靠
最可靠的还是先乘再除看是否还是原数
  1. @echo off&setlocal
  2. :loop2
  3. set /p num=请输入大于1的正整数:
  4. echo.
  5. set count=0
  6. set temp=1
  7. :loop
  8. set /a count+=1
  9. set /a temp1=temp
  10. rem 为了避免set/a的对超大的溢出数自动纠错,直接引用原数,并屏蔽可能的错误输出
  11. set /a temp*=%num% 2>nul
  12. set /a temp2=temp/%num% 2>nul
  13. if "%temp1%"=="%temp2%" goto :loop
  14. set /a count-=1
  15. echo %num%的%count%次方接近于cmd的最大值。
  16. echo.
  17. goto :loop2
复制代码
天的白色影子

TOP

正算法:
  1. @echo off&setlocal enabledelayedexpansion 2>nul 3>nul
  2. set n=1
  3. set /p num=请输入大于1的正整数:
  4. for /l %%a in (1 1 31) do (
  5. set /a n*=num,"max+=^!((n-1)>>31)","test=1/n+1/(!max!-max)"||goto re
  6. )
  7. :re
  8. echo %max%
  9. pause
复制代码
反算法:
  1. @echo off&setlocal enabledelayedexpansion 2>nul 3>nul
  2. set n=2147483648
  3. set /p num=请输入大于1的正整数:
  4. for /l %%a in (1 1 31) do (
  5. set /a n/=num,"max+=^!((n-1)>>31)","test=1/n+1/(!max!-max)"||goto re
  6. )
  7. :re
  8. echo %max%
  9. pause
复制代码
二者联用的话和8楼思路差不多,也是通过比较两个变量大小来判断两种算法的图像相交于何处
1

评分人数

    • qzwqzw: 有心意,有想法技术 + 1

TOP

程序着实有心意
妙处自不多说
有新人自己体会
只提供几个意见查缺补漏

@echo off&setlocal enabledelayedexpansion 2>nul 3>nul

将 2>nul 3>nul的屏蔽错误用在这里确实有新意
不过别忘了程序执行完还要打扫战场的
否则别人在cmd下执行完后
看不到后续所有命令的错误输出时
都不知道该怪谁

31和2147483648两个常数的指定
有些想当然了
万一别人是在64位的cmd环境下呢?

max+=^!((n-1)>>31)
想来是用来判断n的正负性的
转换为0或1后加入max
但是感觉这用法有些多余
直接取%%a不是很省事吗?
只需要在最后再减个1就可以了
而且用除法的话
n可能会溢出为负值吗?
用乘法的话
也有可能直接溢出到正值

test=1/n+1/(!max!-max)
大概是用来判断n是否为0和max是否溢出的
||goto re是遇到错误时跳转
只是看不明白max
是如果未溢出就要跳转吗?
需要详解一下

另外程序没有对超大数进行处理
即如果用户输入的数直接溢出了该怎么办?
天的白色影子

TOP

书接上回
31和2147483648两个常数是不能随意指定
那么如何在cmd下得到这两个常数值呢?

位数可以通过
set /a _d=<一个超级大数>
然后捕捉并分析其错误输出

最大值可以通过
set __d=<一个超级大数>
set /a _d=__d
1

评分人数

    • zm900612: 第一次看见这种取最大数的方法技术 + 1
天的白色影子

TOP

max+=^!((n-1)>>31) 是用于判断n是否小等于0,为0不变,否则加1,直接获取%%a是要绕弯的,不过现在想想确实可以简化成max+=n>>31+1
test=1/n+1/(!max!-max)||goto re 用于进行两个if判断,满足n为0时或者新max变量大于旧max变量两条件之一时跳转到re

楼上方法二巧妙...
{
最大值可以通过
set __d=<一个超级大数>
set /a _d=__d
}

TOP


想通了max的作用和变化了
因为1/n已经判断了n是否为0
所以^!((n-1)>>31)改为^!(n>>31)
只需要判断n是否小于0是可行的
或者将1/n去掉也可以

如果直接取%%a到max
则可以在test中判断n是否小于等于0
set /a max=%%a,"test=1/(^!((n-1)>>31))"

总体来说
是使用set/a的错误抛出
来实现了if的判断跳转

不过正如我在10楼所说
依靠乘方结果的正负性来判断是否溢出是不可靠的
比如100000的乘方结果是1410065408
直接溢出成正值就导致程序判断出错
天的白色影子

TOP

将8楼的代码整理了一下
逻辑上更清晰一些
使可读性更强
并且支持了绝对值大于1的负整数
  1. @echo off&setlocal
  2. :loop2
  3. set /p num=请输入绝对值大于1的整数(可以是负数):
  4. echo.
  5. set count=0
  6. set temp=1
  7. :loop
  8. set /a temp1=temp
  9. rem 为了避免set/a的对超大的溢出数自动纠错,直接引用原数,并屏蔽可能的错误输出
  10. set /a temp*=%num% 2>nul
  11. set /a temp2=temp/%num% 2>nul
  12. rem 如果次方结果乘以整数n再除以整数n不再是原值,则认为乘方结果已接近溢出,跳出
  13. if not "%temp1%"=="%temp2%"                                  goto :jump
  14. if %count% gtr 1024 echo 程序溢出,请确认输入的整数是否正确&pause&goto :eof
  15. set /a count+=1
  16. goto :loop
  17. :jump
  18. echo %num%的%count%次方接近于cmd的最大值。
  19. echo.
  20. goto :loop2
复制代码

[ 本帖最后由 qzwqzw 于 2011-3-14 14:25 编辑 ]
天的白色影子

TOP

楼上说的有点小问题,当时我之所以用max+=^!((n-1)>>31)而不用^!(n>>31)是考虑到2的x次方数的n次方溢出为0的情况,这一点不可不防,所以特意设置一个-1来弥补,用max+=(n-1)>>31+1也可以,大家知道,嗯嗯...

至于溢出导致由正转负再转正,楼上可能没搞明白test的用途,"test=1/n+1/(!max!-max)"这句不仅仅是判断n是否为0,更重要的就是避免溢出被重复计算,!max!是在set /a之前被解释,而max是由set解释,所以!max!-max的效果就是(set前的max  -  当前的max),并借/来判断二者是否相同,相同则跳转。因为不可能存在一个数连续两次乘同样一个数都溢出的情况,所以还没有第二次溢出就已经跳转了。不过现在想想刻意简化成"test=1/n/(!max!-max)"可以

[ 本帖最后由 zm900612 于 2011-3-14 16:37 编辑 ]

TOP

返回列表