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

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+=^!((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

set /a n*=num,"max+=((n-1)>>31)+1","test=1/n/(!max!-max)"||goto re
这句用到了很多技巧,可读性降低,不过却是高效计算的核心代码,可读性在我看来一向是要排在高效、简洁、避免临时文件这几点之后的。

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

TOP

忽然想到这正是判断n是否为2的倍数的最快办法:set /a "test=1/(1073741824*n)"||echo %n%为2的倍数

TOP

已测试,100000结果为1,呵呵,有test的存在是不可能多次溢出的

TOP

巧妙,位运算还是用得太少,没第一时间想到这方向

测试了下正算法,真的有溢出...而且十分诡异,为什么set /a n=100000*100000结果是1410065408??溢出不是应该变成负数么?

TOP

调试了一下,发现100000的溢出很奇怪,以下数据均为  【每次循环的n值   max值】:
请输入大于1的正整数:100000
100000             1
1410065408   2【100000*100000=1410065408???】
-1530494976  2

确切地说是大于46340(约等于极限值的开方)的数可能都有问题:

请输入大于1的正整数:46340
46340              1
2147395600   2
214822976     3【第三次循环时发生溢出,但是不转负】
-837484288    3
302580736     4

TOP

不敢苟同,个人见解如下:
溢出导致由正转负我可以理解,因为2147483647在cmd中的二进制形态为01111111111111111111111111111111,而-1则是1111111111111111111111111111110,所以数值计算时超过2147483648的部分会被当成负数,根本原因就是首位由0转1,才会正负逆转。
但是要发生逆转,必须达到1000000000000000000000000000000000000的数量级,可是有什么正数能同时满足下列方程组呢?
{m+n=32
m+n+n>64}

TOP

哦,我晕,之前糊涂了,这里用的不是加而是乘,如果是加法确实不存在连续两次溢出,乘法溢出的可能就很大了

TOP

再理论化地解释一下:
假设计算范围为8位二进制00000000~11111111,也就是0~128,或者说-64~63,此时:

加法溢出:
十进制:64+64=128
二进制:1000000+1000000=10000000
十进制:65+65=130
二进制:1000001+1000001=10000010
发生溢出,由于二进制倒数第八位为1,所以由正转负,在cmd中分别变为-128和-126

乘法溢出:
十进制:64*64=4096
二进制:1000000*1000000=1000000000000
十进制:65*65=4225
二进制:1000001*1000001=1000010000001
两个算式的结果都超过11111111,于是发生溢出,只获取最后八位,也就是00000000和10000001,换算为cmd中的十进制分别为0与-127

所以结论是,当数值在n*2*128~(n*2+1)*128-1范围内时为正,而在(n*2-1)*128-1~(n*2+1)*128范围内时为负,不知道有没有讲错,反正大概意思是这样。
乘法之所以会出现同一个数值乘某数后,正数溢出仍为正数是因为数值前后跨度超过256,于是溢出之后再溢出,负负得正。这在加法中是不可能出现的,我之前脑筋歪了,把乘法当加法去了

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

TOP

正如27楼所言,咱俩说的恰恰是同一个事

TOP

嗯,笔误,呵呵&nbsp;

TOP

合并算式的时候要注意预处理,要不然变量并不一定会按照我们所想的顺序去拓展
        set /a "varup=!varup!*!n!,vardn=!vardn!/!n!","var=vardn/varup"

TOP

嗯,忘了溢出后的-2147483648是合法的,不过也好办,改成:
set /a "test=1/(1073741824*2*n)"||echo %n%

TOP

返回列表