
标题: 批处理脚本实现C语言趣味编程百例011:打鱼还是晒网 [打印本页]
作者: HAT 时间: 2012-3-31 15:38 标题: 批处理脚本实现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语言中判断能否整除可以使用求余运算(即求模)
*程序说明与注释- #include<stdio.h>
- int days(struct date day);
- struct date{
- int year;
- int month;
- int day;
- };
-
- int main()
- {
- struct date today,term;
- int yearday,year,day;
- printf("Enter year/month/day:");
- scanf("%d%d%d",&today.year,&today.month,&today.day); /*输入日期*/
- term.month=12; /*设置变量的初始值:月*/
- term.day=31; /*设置变量的初始值:日*/
- for(yearday=0,year=1990;year<today.year;year++)
- {
- term.year=year;
- yearday+=days(term); /*计算从1990年至指定年的前一年共有多少天*/
- }
- yearday+=days(today); /*加上指定年中到指定日期的天数*/
- day=yearday%5; /*求余数*/
- if(day>0&&day<4) printf("he was fishing at that day.\n"); /*打印结果*/
- else printf("He was sleeping at that day.\n");
- }
-
- int days(struct date day)
- {
- static int day_tab[2][13]=
- {{0,31,28,31,30,31,30,31,31,30,31,30,31,}, /*平均每月的天数*/
- {0,31,29,31,30,31,30,31,31,30,31,30,31,},
- };
- int i,lp;
- lp=day.year%4==0&&day.year%100!=0||day.year%400==0;
- /*判定year为闰年还是平年,lp=0为平年,非0为闰年*/
- for(i=1;i<day.month;i++) /*计算本年中自1月1日起的天数*/
- day.day+=day_tab[lp][i];
- return day.day;
- }
复制代码
*运行结果
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.
*思考题
请打印出任意年份的日历
作者: terse 时间: 2012-4-2 17:16
本帖最后由 terse 于 2012-4-3 02:20 编辑
回复 3# HAT - @echo off&setlocal enabledelayedexpansion
- set/p Dates=输入年月日(空格隔开)
- for /f "tokens=1-3" %%i in ("%Dates%") do set /a Y=%%i,M=100%%j%%100,D=100%%k%%100
- set _0=打鱼&set _1=晒网
- 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"
- echo !_%JD%!
- pause
复制代码
作者: HAT 时间: 2012-4-2 21:02
回复 2# terse
我要代码
作者: fatcat 时间: 2012-4-2 21:37
给你代码,我也要分- @echo off & setlocal enabledelayedexpansion
- set /a "p=1990-1+400,di1990_1_1=1990*365+p/4-p/100+p/400"
- for %%a in (y m d) do set /p "%%a=%%a: "
- 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"
- if %r% lss 3 (echo 打鱼) else echo 晒网
复制代码
理解为: 1990.1.1 ~ 1990.1.3 打鱼, 1990.1.4 ~ 1990.1.5 晒网, ...
故余数就是 0 ~ 2 打鱼, 3,4 晒网
日历有: 简明万年历
作者: plp626 时间: 2012-4-3 18:18
本帖最后由 plp626 于 2012-4-3 19:04 编辑
回复 2# terse
没测试;但看上去确实是完美版的 短小精悍;
看了老半天没明白算法思想;
求解释;
计算1900-1-1到指定年份日期之间的天数是如何算的?什么公式?- xm=(m-14)/12 ;1-2月的月份映射为-1;3-12月的月份映射为0
- JD=d-2479968 ;常数来历?
- +
- 1461*(y+4800+xm)/4 ;1461来历?
- +
- 367*(m-2-xm*12)/12 ; 367来历?
- -
- 3*(y+4900+xm)/100/4 ;4900 来历?;貌似这里可以精简
复制代码
作者: CrLf 时间: 2012-4-3 19:17
本帖最后由 CrLf 于 2012-4-3 21:13 编辑
调用函数~- @echo off
- call :date2days 1990 1 1 2012 04 03 var
- set /a var%%=5
- if %var% leq 3 (echo 打鱼) else echo 晒网
- pause&exit
-
-
- :::::::::::::::::::::::::::::日期转天数函数::::::::::::::::::::::::::::
- :date2days
- :: 用法1:计算从 1970-01-01 到指定日期之间所经过的天数对应的日历日期。
- :: call :Date2Days %yy% %mm% %dd% days
- :: 参数:%1 待转换的年
- :: %2 待转换的月,可以以零开头
- :: %3 待转换的日,可以以零开头
- :: %4 该变量用于接收所经过的天数(可为空)
- ::
- :: 用法2:计算两个日期的天数差值
- :: call :Date2Days %y1% %m1% %d1% %y2% %m2% %d2% days
- :: 参数:%1 待转换的年1
- :: %2 待转换的月1,可以以零开头
- :: %3 待转换的日1,可以以零开头
- :: %4 待转换的年2
- :: %5 待转换的月2,可以以零开头
- :: %6 待转换的日2,可以以零开头
- :: %7 该变量用于接收所经过的天数(可为空)
- ::
- :: 注:可选项若为空,则显示结果到屏幕,否则把结果存储到相应变量中
- ::
- :: Made by Crlf http://bbs.bathome.net
- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
- setlocal disabledelayedexpansion
- 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
- (endlocal&set %7=%days%||set %4=%days%||echo %days%
- if %6. neq . call %0 %4 %5 %6 %7 %days%) 2>nul
- if /i %0==:date2days exit/b0
复制代码
函数未独立发帖,仅包含于此贴压缩包:http://www.bathome.net/thread-15186-1-4.html
作者: CrLf 时间: 2012-4-3 19:22
本帖最后由 CrLf 于 2012-4-3 21:30 编辑
该函数在区分大小月时采用了独创的算法,所以比较简洁
就题解题就改成这样:- @echo off
- set /a y=2012,m=4,d=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
- if %var% leq 3 (echo 打鱼) else echo 晒网
- pause
复制代码
其中用了大量的 ~-N,其实它与 N-1 等效(同理,-~N 与 N+1 等效),但由于 ~ 与 -(负号) 的优先级高于乘除运算,故 ~-N 可节省一对括号
当然还可以再简洁点,不过估计更没人看得懂了...- @echo off
- set /a y=2012,m=4,d=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 打鱼
- 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- d-726467
- rem 用日减去 1970-01-01~1990-01-01 之间的天数
- 30*m+m/9*-~m/2+!(m/9)*m/2
- rem 先将月份统一乘 30 天,若m大于9,则将日期减去月份加1的一半,否则减去月份的一半
- !!(m/3)*(!(y%%4)-!(y%%100)+!(y%%400)-2)
- rem 当月份大于2的时候,天数减2,还要判断年份是否为闰年,如果是则加1
- y*365+~-y/4-~-y/100+~-y/400-396
- rem 先将年份统一乘365天,再减去闰年的数量
- -396
- rem 消除年月均从0开始计算产生的多余天数
- var%%=5
- rem 将所得的日期偏移量求余
复制代码
作者: HAT 时间: 2012-4-3 20:08
回复 4# fatcat
各位大神详细讲解一下自己的日期算法吧
作者: HAT 时间: 2012-4-3 20:09
回复 6# CrLf
各位大神详细讲解一下自己的日期算法吧
作者: terse 时间: 2012-4-4 01:55
回复 5# plp626
用的计算儒略日数公式 当然还有许多其它公式
作者: plp626 时间: 2012-4-13 20:59
本帖最后由 plp626 于 2012-4-13 21:06 编辑
为这个代码,费神不少,彻底搞懂了;- @echo off&setlocal enabledelayedexpansion
- set 这天=2012-4-13
- set _0=打渔&set _1=晒网
-
- for /f "tokens=1-3 delims=-" %%a in ("%这天%")do set/a y=%%a,m=%%b,d=%%c
- set/a "m=(m+9)%%12,y-=m/10+1989,i=(y/4+(m*153+2)/5+d)%%5/3"
-
- set _%i%!
- pause
复制代码
算法见:
http://www.bathome.net/thread-16147-1-1.html
欢迎光临 批处理之家 (http://bbs.bathome.net/) |
Powered by Discuz! 7.2 |