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

这里不考虑 terse 提出的历法变化问题,

42 楼给出的测试方式是 假定一个 简单易懂易实现的 日期生成 算法 是正确的, 用它从 某个日期 开始依次生成某范围内所有后续的日期.
这样生成的一个日期序列的序号对应着整数序列的一部分,

index = date2index(测试起始日期)
loop (简单算法生成 y.m.d,  直到测试终止日期, (y.m.d)++) {
  y.m.d   ->  i = date2index(y.m.d)   ->   yy.mm.dd = index2date(i)
  上过程中: (i ≠ index)  或者 (yy.mm.dd ≠ y.m.d) 即发现错误
  index++
}

bat 测试确实很慢, 还好没事开着不管它, 写成高级语言的也容易, 下面是已经完成测试过的 bat:
  1. @echo off & setlocal enabledelayedexpansion
  2. REM Index := date2index(1.1.1);
  3. set /a "y=1,m=1,d=1"
  4. set /a "m+=9, m%%=12,y-=m/10, i=365*y + y/4 - y/100 + y/400 + (m*306 + 5)/10 + d - 1"
  5. set /a "Index=i"
  6. for /l %%y in (1 1 20000) do (
  7.   set "y=%%y"
  8.   set /a "leap = (^!(%%y %% 4) & ^!^!(%%y %% 100)) | ^!(%%y %% 400)"
  9.   if "!leap!"=="1" (set "eFeb=29") else set "eFeb=28"
  10.   for %%t in ("1 31" "2 !eFeb!" "3 31" "4 30" "5 31" "6 30" "7 31" "8 31" "9 30" "10 31" "11 30" "12 31") do (
  11.     for /f "tokens=1,2" %%m in ("%%~t") do (
  12.       set "m=%%m"
  13.       for /l %%d in (1 1 %%n) do (
  14.         set "d=%%d"
  15.         REM testing...
  16.         REM if date2index(y.m.d) <> Index (
  17.           REM error
  18.         REM )
  19.         REM if index2date(date2index(y.m.d)) <> y.m.d (
  20.           REM error
  21.         REM )
  22.         REM Index += 1;
  23.         set /a "m+=9, m%%=12,y-=m/10, i=365*y + y/4 - y/100 + y/400 + (m*306 + 5)/10 + d - 1"
  24.         if "!i!" neq "!Index!" ((echo error at %%y.%%m.%%d date2index(y.m.d^)=!i!^<^> !Index!) & pause)
  25.         set /a "y=(i*99+145)/36159,y+=i-365*y-y/4+y/100-y/400>>31,d=i-365*y-y/4+y/100-y/400,m=(5*d+2)/153,d+=1-(153*m+2)/5,y+=m/10,m=(m+2)%%12+1"
  26.         set /a "error=(y-%%y)|(m-%%m)|(d-%%d)"
  27.         if "!error!" neq "0" ((echo error at %%y.%%m.%%d index2date(date2index(y.m.d^)^)=!y!.!m!.!d!) & pause)
  28.         echo %%y.%%m.%%d  Index=!Index!
  29.         set /a "Index+=1"
  30.       )
  31.     )
  32.   )
  33. )
  34. pause
  35. exit
复制代码

TOP

进一步测试发现,i2date函数很是问题;

bug丛生;

问题出在 i2date函数的第一句; 不是随便可以精简的。。。

TOP

本帖最后由 plp626 于 2012-4-11 21:02 编辑

C测试,40万年以内测试用时秒杀
  1. //&cls&@type %~fs0|tcc -run -
  2. // 把tcc.exe 和 lib\msvcrt.def 放在当前目录下;双击本脚本解释执行下面C代码
  3. int i2date(int i, int *, int *, int *);
  4. int date2i(int, int, int);
  5. /*
  6. * 把0-3-1作为参考日历{0-0-0}的0号索引,后面依次类推;
  7. * 参考日历月份数为 0,1,2,...,11;
  8. * 11月分闰月和平月;当所在年份+1为闰年时为闰月;
  9. * 参考日历日期数为 0,1,...,30;
  10. * 大月最大偏移30,小月最大偏移29;
  11. * 11月中,闰月最大偏移28;平月最大偏移27;
  12. */
  13. int main(){
  14. int i,j;
  15. int y,t,m,d;
  16. for (i=0; i<1461*100000; i++){ // 0 ~ 40万年索引号
  17. i2date(i,&y,&m,&d);
  18. j=date2i(y,m,d);
  19. if (i!=j) { // 测试,寻找不对称转换的具体值。。
  20. printf("/%5d:%4d/%2d/%2d\n",i,y,m,d);
  21. getchar();
  22. }
  23. }
  24. return 0;
  25. }
  26. int i2date(int i, int *year, int *month, int *day){ // 索引转日期
  27. int t,y,m,d;
  28. y=(i*4+999)/1461;          // 1/365.2425的前6位小数最佳有理逼近
  29. // y=(i*99+145)/36159;
  30. // y=i*33/12053;            //1/365.2425的前9位小数最佳有理逼近
  31. t=i-y*365-y/4+y/100-y/400;
  32. y+=t>>9; // 获得参考年份;若t为负数,将参考年减1
  33. t=i-y*365-y/4+y/100-y/400;  // 获得参考年0月0日的偏移数
  34. m=(t*5+2)/153; // 获得参考月份
  35. d=t-(m*153+2)/5; // 获得参考日期
  36. d=d+1; // 获得实际日期
  37. y+=(m+2)/12; // 获得实际年份
  38. m=(m+2)%12+1; // 获得实际月份
  39. *year=y;
  40. *month=m;
  41. *day=d;
  42. return 0;
  43. }
  44. int date2i(int y, int m, int d){// 日期转索引
  45. int i;
  46. m+=9;
  47. m%=12;
  48. y-=m/10;
  49. //上面三句做平移取模,3月作为当年的0月;2月作为上1年的11月;
  50. i=365*y+y/4-y/100+y/400+(m*153+2)/5+d-1;
  51. // 11月初的偏移为337;337/11≈153/5;
  52. // (m*153+2)/5 将 {0,1,2,...,11}映射为{0,31,61,...,337}
  53. return i;
  54. }
复制代码
可是,这样的对称测试 有意义吗? 即使在date2index正确的条件下,也存在index2date出错;

即使date2index正确,date2index它也只是保证正确的日期生成正确的索引;
错误的日期也有可能生成正确的索引;2002-2-29号是错的,但它在意义上式2002-3-1;生成的索引也是2002-3-1日的索引;
如此以来,还需验证index2date生成正确的日期。。。

TOP

本帖最后由 plp626 于 2012-4-11 20:59 编辑

回复 40# neorobin
  1. @echo off
  2. :date2i <year> <month> <day> // 显示year-month-day 所对应的索引值;
  3. setlcoal&set/a y=%1,m=%2,d=%3
  4. set/a m+=9
  5. set/a m%%=12
  6. set/a y-=m/10
  7. set/a i=365*y+y/4-y/100+y/400+(m*153+2)/5+d-1
  8. endlocal&echo %i%
复制代码
  1. @echo off
  2. :i2date <index> // 显示 %1 所对应的日期 %1 取值范围0~2千万
  3. setlocal&set/a i=%1
  4. set/a y=(i*99+145)/36159
  5. set/a t=i-y*365-y/4+y/100-y/400
  6. set/a "y+=t>>9"
  7. set/a t=i-y*365-y/4+y/100-y/400  
  8. set/a m=(t*5+2)/153
  9. set/a d=t-(m*153+2)/5
  10. set/a d=d+1
  11. set/a y+=(m+2)/12
  12. set/a m=(m+2)%%12+1
  13. endlocal&echo %y%-%m%-%d%
复制代码
在索引号转日期的函数i2date中,(i*99+145)/36159
这个组合保证 [0~21691753)以内的索引;即[0-3-1 ~ 59208-9-2);之间的正确性;

(i*4+999)/1461 这个组合可以保证 [0~12200561 )以内的索引; 即[0-3-1 ~ 33404-3-1)之间的所有日期;

进一步发现
i*33/12053
这个组合可以保证[0~65075232) 以内;即[0-3-1 ~ 177625-3-7) 之间的所有日期;
error: /65075263:177625/ 9/ 9

以上结论只代表在假定函数date2index正确的情况下,没有报错的测试结果;

如果正确,date2index和 index2date两个函数轻松搞定了有关时间日期计算(0-3-1后)的所有问题;

TOP

回复 44# terse




    100-2-28 的下一天是 100-2-29 还是 100-3-1

如果是前者,那就不是现在的历法,背后有什么故事么。。。?

TOP

回复 44# terse

惭愧, 对儒略日, 历法变化等问题完全没有了解或者了解甚少, 前面都完全是按一个固定的历法规则处理的, 如要适应相关的 规则约定 或者 实际情况, 肯定要作出进一步的修改.

TOP

回复 42# neorobin
以此代码推算至100.2.28后就是100.3.1按实历算的话应该是100.2.29后才是100.3.1
这里是否考虑实历1582前后问题 即1582前每4年一闰  1582后才多了100和400余
也就是说如果这里仅仅代表一种序号的话 那这个序号的合理性应该是计算1582年10月15号后的
譬如 1582年10月4号 儒略日 (2299160)  后一日 便直接跳至1582年10月15日 儒略日 (2299161)
也可能个人理解的不同  故一点浅见而已
1

评分人数

    • CrLf: 强大的一群技术 + 1

TOP

bat测试太慢;
set/a 表达式等同C的算术表达式;
这里给个超级精简版的C编译器,方便大家测试用;
附件: 您需要登录才可以下载或查看附件。没有帐号?注册
1

评分人数

    • neorobin: 谢谢,可是我已经用 BAT 测试到 17700 多了, ...技术 + 1

TOP

本帖最后由 neorobin 于 2012-4-10 17:42 编辑

回复 41# plp626
  1. @echo off & setlocal enabledelayedexpansion
  2. REM nextIndex := date2index(1.1.1);
  3. for /l %%y in (1 1 20000) do (
  4.   set "y=%%y"
  5.   set /a "leap = (^!(%%y %% 4) & ^!^!(%%y %% 100)) | ^!(%%y %% 400)"
  6.   if "!leap!"=="1" (set "eFeb=29") else set "eFeb=28"
  7.   for %%t in ("1 31" "2 !eFeb!" "3 31" "4 30" "5 31" "6 30" "7 31" "8 31" "9 30" "10 31" "11 30" "12 31") do (
  8.     for /f "tokens=1,2" %%m in ("%%~t") do (
  9.       set "m=%%m"
  10.       for /l %%d in (1 1 %%n) do (
  11.         set "d=%%d"
  12.         REM testing...
  13.         REM if date2index(y.m.d) <> nextIndex (
  14.           REM error
  15.         REM )
  16.         REM if index2date(date2index(y.m.d)) <> y.m.d (
  17.           REM error
  18.         REM )
  19.         REM nextIndex += 1;
  20.         echo !y!.!m!.!d!
  21.       )
  22.     )
  23.     pause
  24.   )
  25. )
复制代码
和 13 楼说的差不多, 我没想到可以证明正确的方式:

设想一个并不聪明的一定范围内穷举式的测试方式:

公历的历法是明确的:
每年 12 个月: m∈[1..12]
4,6,9,11 月份固定 30 天, d ∈[1..30]
2 月根据年份平闰取 28 天或 29 天,  d∈[1..29]
其余月份固定 31 天, d∈[1..31]

闰年:
(被 4 整除 且 不被 100 整除) 或 被 400 整除 的年份
闰年外的年份都是平年

从这个公历历法的原型定义出发设计一个 尽可能直接符合 且 足够简单 的日期生成算法(假定它是正确的算法):
从一个初始日期开始, 依次生成每一个明天,
把这个算法生成的日期作为 date2index 的参数, 得到相应的 序号:

i_0: 初始日期的序号(值由算法自定);   (_0 表示 0 为下标)

i_p: 初始日期 或者 初始日期之后的 某个日期的序号;
i_q: i_p 对应的那个日期的下一日的序号;

那么, 如果对于任意 i_p 与 i_q, 都得到:

i_q = i_p + 1

就可以把 date2index 看作是测试范围内正确的算法.

在上面的测试中可以同时 作 index2date 测试:
如果始终都有:
index2date( date2index(y.m.d) ) = y.m.d

在 date2index 测试正确的情况下, 就可以认为 index2date 也是正确的.


date2index 和 index2date 在任意参数的情况下的输出都是唯一的, 且是确定的(对于一个确定的输入 I, 不会得到 O1, O2两种或更多可能的输出),

因为算法是基于 数字计算机 的, 输入参数 不具备不确定性(每次输入的值的集合都是确定的),
且算法中不涉及随机函数的调用, 所以 输出是 具有 唯一性和确定性的.

TOP

有一个问题
如何测试可以断定date2index一定正确?
如何测试可以断定index2date一定正确?

在date2index都不能断定是正确的情况下,
那么,测试index2date正确性的时候,去调用date2index得出的结论应该是不确定的。。

TOP

回复 39# plp626


    弄在一行了, 不必要的变量都省了, 137 字节
  1. set /a "y=(i*99+145)/36159,y+=i-365*y-y/4+y/100-y/400>>31,d=i-365*y-y/4+y/100-y/400,m=(5*d+2)/153,d+=1-(153*m+2)/5,y+=m/10,m=(m+2)%%12+1"
复制代码

TOP

回复 36# neorobin

其实你很容易就能精简到140字节以内。。。

TOP

回复 34# Batcher

事实上我没有上过电脑课, Pascal: 端庄典雅秀丽的淑女; C: 自由奔放不羁的野马

TOP

本帖最后由 neorobin 于 2012-4-10 02:54 编辑

回复 35# terse


    不错, 我一直测试到 7305155 没有发现错误

TOP

回复 33# plp626

把 25 楼的代码改了一点点, 没有实质性的变化, 凑成 2 行 155 字节:
  1. set /a "y=(i*99+145)/36159,y+=i-365*y-y/4+y/100-y/400>>31,dd=i-365*y-y/4+y/100-y/400"
  2. set /a "mi=(5*dd+2)/153,m=(mi+2)%%12+1,y-=m-3>>31,d=1+dd-(153*mi+2)/5"
复制代码

TOP

返回列表