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


{1,2,3} <--> {2012.4.12, 2012.4.16, 2012.4.30}

形如上面的互逆映射关系, 也许这里我们讨论的算法不会形成, 但如果某算法造成这样的关系, 那么不能保证日期的连续增 1 的检测方式是检测不出那种算法的错误的.

TOP

本帖最后由 plp626 于 2012-4-12 15:34 编辑

序号是单调增,也就是说日期必须逐一累加才能得到序号;

如果日期有跳跃,那前面的日期必然出现天数多了出来;
只需验证i2date生成的日期正确即可:
  1. //&cls&@type %~fs0|tcc -run -
  2. // 把tcc.exe 和 lib\msvcrt.def 放在当前目录下;双击本脚本解释执行下面C代码
  3. int i2date(int , int *, int *, int *);
  4. int date2i(int, int, int);
  5. int isdate(int, int, int);
  6. int main(){
  7. int i,j,y,t,m,d;
  8. for (i=0; i<1461*100000; i++){ // 0 ~ 40万年索引号
  9. i2date(i,&y,&m,&d);
  10. if (!isdate(y,m,d)){
  11. printf("wrongdate: /%5d:%4d/%2d/%2d\n",i,y,m,d);
  12. getchar();
  13. }
  14. j=date2i(y,m,d);
  15. if (i!=j) {
  16. printf("/%5d:%4d/%2d/%2d\n",i,y,m,d);
  17. getchar();
  18. }
  19. }
  20. return 0;
  21. }
  22. int isdate(int y, int m, int d){
  23. int mi[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
  24. if ((y%4 == 0) && (y%100 != 0) || (y%100 == 0))
  25. mi[2]+=1;
  26. if (m<=0||m>12)
  27. return 0;
  28. if (d<=0||d>mi[m])
  29. return 0;
  30. return 1;
  31. }
  32. int i2date(int i, int *year, int *month, int *day){ // 索引转日期
  33.    int t,y,m,d;      y=(i*33+999)/12053;
  34.     t=i-y*365-y/4+y/100-y/400;  y+=t>>9;  t=i-y*365-y/4+y/100-y/400;
  35.     m=(t*5+2)/153;  d=t-(m*153+2)/5+1;
  36.     y+=(m+2)/12;  m=(m+2)%12+1;
  37.    *year=y;  *month=m;  *day=d;
  38.     return 0;
  39. }
  40. int date2i(int y, int m, int d){// 日期转索引
  41. m+=9;m%=12; y-=m/10;
  42. return 365*y+y/4-y/100+y/400+(m*153+2)/5+d-1;
  43. }
复制代码

TOP

回复 64# plp626

如果只是以 由序号得到的日期是否合法来测试 index2date, 我认为是不妥的:

比如: 由 i 得到了 2012.4.10, 而接着 i+1 得到的却是 2011.4.11, ??? 日期都是合法的, 但序列乱了, 也许可以证明出现 序列乱了 也必然出现非法日期, 但至少现在没有证明这一点

TOP

本帖最后由 plp626 于 2012-4-12 14:55 编辑

加入日期正确性验证;17万年内顺利通过验证;
完美组合:
  1. y=(i*33+999)/12053 //索引反求日期中年份的
复制代码
  1. int isdate(int y, int m, int d){
  2. int mi[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
  3. if ((y%4 == 0) && (y%100 != 0) || (y%100 == 0))
  4. mi[2]+=1;
  5. if (m<=0||m>12)
  6. return 0;
  7. if (d<=0||d>mi[m])
  8. return 0;
  9. return 1;
  10. }
复制代码
  1. (i*4+99)/1461; 3301-2-28以内正确;
复制代码
  1. (i*4+999)/1461; 33301-2-28以内正确;
复制代码
---------------

需要说明的是最上面的报错测试,17万年出错是因为cmd 的最大数字限制;2147483647 溢出导致的报错;

TOP

回复 60# neorobin


    我的测试结果和你不一致;问题比较大。。。

TOP

回复 59# plp626

测试代码确实有问题, 是记录 误差最小值的一个变量的初始值设小了,  但 93 这个常数还是碰对了

修改后:
   
93
The pOffsMin is: 0.000684;  the pOffsMax is: 0.063655
The pMin is: 3000;  the pMax is: 4
pOffsMin = 0.000684,  pOffsMax + 365*4 / 1461 = 1.062971



0
The pOffsMin is: -0.062971;  the pOffsMax is: 0.000000
The pMin is: 3000;  the pMax is: 4
pOffsMin = -0.062971,  pOffsMax + 365*4 / 1461 = 0.999316

0.062971 * 1461 = 92.000631

第一次出错的发生:
3002.2.28  Index=1096456
error at 3002.3.1 index2date(date2index(y.m.d))=3002.2.29

TOP

你测试 (i * 4 + 93) / 1461 兼容的最大范围是?

TOP

本帖最后由 neorobin 于 2012-4-11 23:32 编辑

回复 57# plp626


    我测试的结果是 [1,3000] 内,  (i * 4 + 93) / 1461 就可行

(i * 4 + 0) / 1461
The pOffsMin is: -0.062971;  the pOffsMax is: 0.000000
The pMin is: 3000;  the pMax is: 4
pOffsMin = -0.062971,  pOffsMax + 365*4 / 1461 = 0.999316

0.062971 * 1461 = 92.000631

(i * 4 + 93) / 1461
The pOffsMin is: 0.000000;  the pOffsMax is: 0.063655
The pMin is: 4;  the pMax is: 4
pOffsMin = 0.000000,  pOffsMax + 365*4 / 1461 = 1.062971

TOP

回复 51# neorobin


    我相信那个比值 (33*i+x)/12053可代替 (i*99+145)/36159;且范围不会大幅缩减;

找3000年以内的;我考虑 (4*i+x)/1461

TOP

针对1582 10 4 ----1582 10 15 的测试
  1. @echo off&setlocal enabledelayedexpansion
  2. (FOR /l %%i in (2298883 1 2299603) DO (
  3.     set JD=%%i
  4.     IF !JD! GEQ 2299161 set /a "JD+=1+(JD*100-186721625)/3652425-(JD*100-186721625)/3652425/4"
  5.     set /a "B=JD+1524,Y=(B*100-12210)/36525,D=36525*Y/100"
  6.     set /a "M=(B-D)*100/3061,D=B-D-3061*M/100,M=(M-2)%%12+1,Y-=4715+^!(2/M)"
  7.     set "str=!Y!:!M!:!D!"
  8.     set /a "M=(M+9)%%12+3,Y-=M/13,JD=36525*(Y+4716)/100+3061*(M+1)/100+D-1524"
  9.     REM 1582年10月4日后重新计算闰年,先前每4年一闰.
  10.     set /a "JD+=(2-Y/100+Y/400)*^!(2299161/JD)"
  11.     ECHO !str! !JD!
  12. ))>Yjd.txt
  13. start "" "Yjd.txt"
  14. PAUSE
复制代码
  1. @echo off&setlocal enabledelayedexpansion
  2. for %%i in (31 28 31 30 31 30 31 31 30 31 30 31) do set /a N+=1&set "M_!N!=%%i"
  3. set JDX=2298883
  4. for /l %%i in (1582 1 1583) do (
  5.      IF %%i gtr 1582 (
  6.         set /a "N=^!(%%i%%4)^^^!(1582/%%i)&^!(%%i%%400)^^^!(1582/%%i)&^!(%%i%%100)"
  7.      ) else set /a "N=^!(%%i%%4)"
  8.      set /a M_2=28+N
  9.      for /l %%j in (1 1 12) do (
  10.          set /a "M=(%%j+9)%%12+3,Y=%%i-M/13"
  11.          for /l %%k in (1 1 !M_%%j!) do (
  12.              set /a "JD=36525*(Y+4716)/100+3061*(M+1)/100+%%k-1524,JDX+=1"
  13.              set /a "JD+=(2-Y/100+Y/400)*^!(2299161/JD)"  2>nul
  14.              IF !JDX! NEQ !JD! set ERR=!err!$err:%%i:%%j:%%k !JD! !JDX!$
  15.              REM 1582年10月4日后初始日须减10天
  16.              if "%%i:%%j:%%k" == "1582:10:5" set/a JDX-=10
  17.          )
  18.      )
  19. )
  20. IF DEFINED ERR echo !err:$=^
  21. !
  22. ECHO !JD! !JDX!
  23. pause
复制代码

TOP

i2date中,
  1. y=(i*99+145)/36159;
复制代码
通过9999年以内的索引转日期测试;
我用matlab生成标准日期文件69M+;
再用测试代码用C生成了标准日期文件69M+;
两个文件md5值相同均为 a6740e69a45b825334b8c4c4c986fe2f;

标准打印格式:

fid 模式wt;
fprintf(fid,"%7d:%.4d/%.2d/%.2d\n",i,y,m,d);
末行 3652060:9999/03/02

索引范围 0~3652060 [0-3-1 ~ 9999-3-2]

TOP

本帖最后由 neorobin 于 2012-4-11 23:10 编辑

回复 53# plp626


    用个能算到 公元 3000 年的历法就蛮足够了, 往过去可以算 100 年也就可以了, 我们不考古, 呵呵,  我用过最大的用途就是看下出生日期什么的.


51 楼纠正一下, 应说成 整数误差绝对值 跨度 将达到 2: 例如, 应该得到的 年份是 2012, 但误差范围可能出现 3 种(据算法择其中一种)可能的区间 [2010,2012], [2011,2013], [2012,2014], 尽管这个误差正峰值和负峰值并不会发生在对同一个年份的估值上, 但代码上总是要对这两个峰值作统一处理的.

TOP

回复 52# terse


不同年份阶段,要不同的历法,    太复杂了; 很不好计算;

反正matlab里面100-2-28的下一天是100-3-1;
  1. >> datestr(36584,26)
  2. ans =
  3. 0100/02/28
  4. >> datestr(36585,26)
  5. ans =
  6. 0100/03/01
  7. >>
复制代码

TOP

回复 46# plp626
100-2-28 的下一天是 100-2-29
这里说的公元100年2月应该有29天
前面我提到 公元1582年10月4号前是每4年一闰  不管年份是否可被100或400整除
1582年10月由格勒哥里十三世改革成为格勒哥里历,取消1582年10月5日至1582年10月14日这10日及取消400年内00年尾的3个闰年,使一年的平均日数变成365.2425日,更接近于准确的回归年365.2422日。
格勒哥里历(也就是现在大多使用的):
每4年置闰年一次,闰年366日,二月29日.凡年份数可被4 整除者,置闰. 如1960,1996等.凡年份数可被100整除者,不置闰, 如1800,1900.凡年份数可被400整除者,置闰, 如2000,2400.

TOP

本帖最后由 neorobin 于 2012-4-11 23:02 编辑

回复 49# plp626

31 楼, 我做了年份估值误差范围测试, 如果估值函数在给定计算范围内的 估值误差范围 跨过 两个整数(比如 [-0.1,1.3] 跨过了 0 和 1 两个整数), 就不利于后续代码, 后面年份的误差调整就不是简单的只作出 要么不变, 要么加1(或者减1) 这样的方式了, 因为 跨过两个整数 意味着误差的整数绝对值跨度将达到 2.

TOP

返回列表