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

批处理脚本实现C语言趣味编程百例011:打鱼还是晒网

加分:1个技术分或者10个PB

要求用BAT脚本实现:

中国有句俗语叫“三天打鱼两天晒网”。某人从1990年1月1日起开始“三天打鱼两天晒网”,问这个人在以后的某一天中是“打鱼”还是“晒网”。

*问题分析与算法设计
根据题意可以将解题过程分为三步:
1)计算从1990年1月1日开始至指定日期共有多少天;
2)由于“打鱼”和“晒网”的周期为5天,所以将计算出的天数用5去除;
3)根据余数判断他是在“打鱼”还是在“晒网”;
若 余数为1,2,3,则他是在“打鱼”
否则 是在“晒网”
在这三步中,关键是第一步。求从1990年1月1日至指定日期有多少天,要判断经历年份中是否有闰年,二月为29天,平年为28天。闰年的方法可以用伪语句描述如下:
如果 ((年能被4除尽 且 不能被100除尽)或 能被400除尽)
则 该年是闰年;
否则 不是闰年。
C语言中判断能否整除可以使用求余运算(即求模)

*程序说明与注释
  1. #include<stdio.h>
  2. int days(struct date day);
  3. struct date{
  4. int year;
  5. int month;
  6. int day;
  7. };
  8. int main()
  9. {
  10. struct date today,term;
  11. int yearday,year,day;
  12. printf("Enter year/month/day:");
  13. scanf("%d%d%d",&today.year,&today.month,&today.day); /*输入日期*/
  14. term.month=12; /*设置变量的初始值:月*/
  15. term.day=31; /*设置变量的初始值:日*/
  16. for(yearday=0,year=1990;year<today.year;year++)
  17. {
  18. term.year=year;
  19. yearday+=days(term); /*计算从1990年至指定年的前一年共有多少天*/
  20. }
  21. yearday+=days(today); /*加上指定年中到指定日期的天数*/
  22. day=yearday%5; /*求余数*/
  23. if(day>0&&day<4) printf("he was fishing at that day.\n"); /*打印结果*/
  24. else printf("He was sleeping at that day.\n");
  25. }
  26. int days(struct date day)
  27. {
  28. static int day_tab[2][13]=
  29. {{0,31,28,31,30,31,30,31,31,30,31,30,31,}, /*平均每月的天数*/
  30. {0,31,29,31,30,31,30,31,31,30,31,30,31,},
  31. };
  32. int i,lp;
  33. lp=day.year%4==0&&day.year%100!=0||day.year%400==0;
  34. /*判定year为闰年还是平年,lp=0为平年,非0为闰年*/
  35. for(i=1;i<day.month;i++) /*计算本年中自1月1日起的天数*/
  36. day.day+=day_tab[lp][i];
  37. return day.day;
  38. }
复制代码
*运行结果
Enter year/month/day:1991 10 25
He was fishing at day.
Enter year/month/day:1992 10 25
He was sleeping at day.
Enter year/month/day:1993 10 25
He was sleeping at day.

*思考题
请打印出任意年份的日历
2

评分人数

本帖最后由 plp626 于 2012-4-13 21:06 编辑

为这个代码,费神不少,彻底搞懂了;
  1. @echo off&setlocal enabledelayedexpansion
  2. set 这天=2012-4-13
  3. set _0=打渔&set _1=晒网
  4. for /f "tokens=1-3 delims=-" %%a in ("%这天%")do set/a y=%%a,m=%%b,d=%%c
  5. set/a "m=(m+9)%%12,y-=m/10+1989,i=(y/4+(m*153+2)/5+d)%%5/3"
  6. set _%i%!
  7. pause
复制代码
算法见:
http://www.bathome.net/thread-16147-1-1.html

TOP

回复 5# plp626
用的计算儒略日数公式  当然还有许多其它公式

TOP

回复 6# CrLf


各位大神详细讲解一下自己的日期算法吧

TOP

回复 4# fatcat


各位大神详细讲解一下自己的日期算法吧

TOP

本帖最后由 CrLf 于 2012-4-3 21:30 编辑

该函数在区分大小月时采用了独创的算法,所以比较简洁
就题解题就改成这样:
  1. @echo off
  2. set /a y=2012,m=4,d=3
  3. set /a var=d-726467+30*m+m/9*-~m/2+!(m/9)*m/2+!!(m/3)*(!(y%%4)-!(y%%100)+!(y%%400)-2)+y*365+~-y/4-~-y/100+~-y/400-396,var%%=5
  4. if %var% leq 3 (echo 打鱼) else echo 晒网
  5. pause
复制代码
其中用了大量的 ~-N,其实它与 N-1 等效(同理,-~N 与 N+1 等效),但由于 ~ 与 -(负号) 的优先级高于乘除运算,故 ~-N 可节省一对括号
当然还可以再简洁点,不过估计更没人看得懂了...
  1. @echo off
  2. set /a y=2012,m=4,d=3
  3. set /a "1/((d-726467-396+30*m+m/9*-~m/2+!(m/9)*m/2+!!(m/3)*(!(y%%4)-!(y%%100)+!(y%%400)-2)+y*365+~-y/4-~-y/100+~-y/400)%%5/4)" 2>nul&&echo echo 晒网||echo 打鱼
  4. pause
复制代码
回复 9# HAT
算法简单解释:
set /a var=d-726863+30*m+m/9*-~m/2+!(m/9)*m/2+!!(m/3)*(!(y%%4)-!(y%%100)+!(y%%400)-2)+y*365+~-y/4-~-y/100+~-y/400,var%%=5
  1. d-726467
  2. rem 用日减去 1970-01-01~1990-01-01 之间的天数
  3. 30*m+m/9*-~m/2+!(m/9)*m/2
  4. rem 先将月份统一乘 30 天,若m大于9,则将日期减去月份加1的一半,否则减去月份的一半
  5. !!(m/3)*(!(y%%4)-!(y%%100)+!(y%%400)-2)
  6. rem 当月份大于2的时候,天数减2,还要判断年份是否为闰年,如果是则加1
  7. y*365+~-y/4-~-y/100+~-y/400-396
  8. rem 先将年份统一乘365天,再减去闰年的数量
  9. -396
  10. rem 消除年月均从0开始计算产生的多余天数
  11. var%%=5
  12. rem 将所得的日期偏移量求余
复制代码
1

评分人数

TOP

本帖最后由 CrLf 于 2012-4-3 21:13 编辑

调用函数~
  1. @echo off
  2. call :date2days 1990 1 1 2012 04 03 var
  3. set /a var%%=5
  4. if %var% leq 3 (echo 打鱼) else echo 晒网
  5. pause&exit
  6. :::::::::::::::::::::::::::::日期转天数函数::::::::::::::::::::::::::::
  7. :date2days
  8. :: 用法1:计算从 1970-01-01 到指定日期之间所经过的天数对应的日历日期。
  9. ::  call :Date2Days %yy% %mm% %dd% days
  10. ::  参数:%1 待转换的年
  11. ::        %2 待转换的月,可以以零开头
  12. ::        %3 待转换的日,可以以零开头
  13. ::        %4 该变量用于接收所经过的天数(可为空)
  14. ::
  15. :: 用法2:计算两个日期的天数差值
  16. ::  call :Date2Days %y1% %m1% %d1% %y2% %m2% %d2% days
  17. ::  参数:%1 待转换的年1
  18. ::        %2 待转换的月1,可以以零开头
  19. ::        %3 待转换的日1,可以以零开头
  20. ::        %4 待转换的年2
  21. ::        %5 待转换的月2,可以以零开头
  22. ::        %6 待转换的日2,可以以零开头
  23. ::        %7 该变量用于接收所经过的天数(可为空)
  24. ::
  25. :: 注:可选项若为空,则显示结果到屏幕,否则把结果存储到相应变量中
  26. ::
  27. ::                                Made by Crlf     http://bbs.bathome.net
  28. :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  29. setlocal disabledelayedexpansion
  30. set/a days=10%~3%%100-719050+30*(m=10%~2%%100)+m/9*-~m/2+!(m/9)*m/2+!!(m/3)*(!((y=%1)%%4)-!(y%%100)+!(y%%400)-2)+y*365+~-y/4-~-y/100+~-y/400-396-%50/10*!%60
  31. (endlocal&set %7=%days%||set %4=%days%||echo %days%
  32. if %6. neq . call %0 %4 %5 %6 %7 %days%) 2>nul
  33. if /i %0==:date2days exit/b0
复制代码
函数未独立发帖,仅包含于此贴压缩包:http://www.bathome.net/thread-15186-1-4.html
1

评分人数

TOP

本帖最后由 plp626 于 2012-4-3 19:04 编辑

回复 2# terse


没测试;但看上去确实是完美版的 短小精悍;
看了老半天没明白算法思想;
求解释;
计算1900-1-1到指定年份日期之间的天数是如何算的?什么公式?
  1. xm=(m-14)/12               ;1-2月的月份映射为-1;3-12月的月份映射为0
  2. JD=d-2479968               ;常数来历?
  3. +
  4. 1461*(y+4800+xm)/4   ;1461来历?
  5. +
  6. 367*(m-2-xm*12)/12    ; 367来历?
  7. -
  8. 3*(y+4900+xm)/100/4  ;4900 来历?;貌似这里可以精简
复制代码

TOP

给你代码,我也要分
  1. @echo off & setlocal enabledelayedexpansion
  2. set /a "p=1990-1+400,di1990_1_1=1990*365+p/4-p/100+p/400"
  3. for %%a in (y m d) do set /p "%%a=%%a: "
  4. set /a "p=y-1+400,m-=1,r=-di1990_1_1+d-1+y*365+p/4-p/100+p/400+m*31+(~(m-4)>>31)+(~(m-6)>>31)+(~(m-9)>>31)+(~(m-11)>>31)+((~(m-2)>>31)&-2-(^!^!(y&3)|(^!^!(y&0xf)&^!0x!y:~-2!))),r%%=5"
  5. if %r% lss 3 (echo 打鱼) else echo 晒网
复制代码
理解为: 1990.1.1  ~ 1990.1.3 打鱼, 1990.1.4 ~ 1990.1.5 晒网, ...
故余数就是 0 ~ 2 打鱼, 3,4 晒网

日历有: 简明万年历
1

评分人数

    • plp626: 都是O(1)型的;技术 + 1

TOP

回复 2# terse


我要代码

TOP

本帖最后由 terse 于 2012-4-3 02:20 编辑

回复 3# HAT
  1. @echo off&setlocal enabledelayedexpansion
  2. set/p Dates=输入年月日(空格隔开)
  3. for /f "tokens=1-3" %%i in ("%Dates%") do set /a Y=%%i,M=100%%j%%100,D=100%%k%%100
  4. set _0=打鱼&set _1=晒网
  5. set /a "JD=(d-2479968+1461*(y+4800+(m-14)/12)/4+367*(m-2-(m-14)/12*12)/12-3*(y+4900+(m-14)/12)/100/4)%%5/3"
  6. echo !_%JD%!
  7. pause
复制代码
1

评分人数

TOP

返回列表