您的挑战是编写一个程序,该程序在给定的一年内输出其中的“星期五13日”数字。
规则和详细信息:
- 您可以通过
STDIN
或作为传递给程序的参数来接受输入。 - 您应该将结果输出到
STDOUT
。 - 您可以假定输入将是有效年份,并且未提前输入公历(在这些情况下允许未定义的行为)。
- 日历/日期库是允许的。
这是一个代码高尔夫球,因此最短的代码(以字节为单位)获胜。
您的挑战是编写一个程序,该程序在给定的一年内输出其中的“星期五13日”数字。
规则和详细信息:
STDIN
或作为传递给程序的参数来接受输入。STDOUT
。这是一个代码高尔夫球,因此最短的代码(以字节为单位)获胜。
Answers:
+/{13∊⍎,⍉3↑¯5↑⍉2↓cal⍵}¨⎕,¨⍳12
⍳ 12
整数1到12
⎕ ,¨
接受数字输入并在十二个数字的前面
{
… }¨
在每对上应用该功能…
cal⍵
获取该年月份的日历
2 ↓
放两行(标题和天)
⍉
转置(因此我们可以寻址列而不是行)
¯5 ↑
取最后五个数字(星期五和星期六每个数字两位,再加上一个空格)
3 ↑
取前两位(星期五两位,另加一个空格)
⍉
转置(以便获得阅读顺序)
,
拉夫
⍎
作为APL表达式执行(提供星期五的日期列表)
13 ∊
那个名单中有十三名吗?
+/
对12个布尔值求和
使用@Wrzlprmft的算法,我们可以不用53个字节的库就可以做到:
'21232211321211'⊃⍨14|2 3 ¯1+.×⊢,0≠.=400 100 4∘.|-∘0 1
-∘0 1
减去零和一
400 100 4 ∘.|
两年(整个)的除法余数表除以这些数字(向下)
0 ≠.=
内部“乘积”为0,但使用≠和=而不是+。×
⊢ ,
在未修改的参数年份之前
2 3 ¯1 +.×
具有这些数字的内部产品
14 |
除数除以十四
'21232211321211' ⌷⍨
索引到这个字符串
作为纯函数:42个字符
DayName@{#,m,6}~Table~{m,12}~Count~Friday&
例
DayName@{#,m,6}~Table~{m,12}~Count~Friday&[2013]
2
作为命名函数:44个字符
f=DayName@{#,m,6}~Table~{m,12}~Count~Friday&
例子
f[1776]
f[2012]
f[2013]
f[2014]
2
3
2
1
f=DayName@{#,m,6}~Table~{m,12}~Count~Friday&
f=->m{(1..12).count{|i|Time.gm(m,i,6).friday?}}
编辑:感谢扬,通过回一周来剃光了角色,并通过从Time.new切换到Time.gm来剃光了另一个角色
编辑:以更多的混淆为代价,我可以达到46
f=->m{(1..12).count{|i|Time.gm(m,i,8).wday<1}}
Time.gm(m,i).wday<1
。另外,我不知道您为什么要命名该功能。
感谢Iszi的提示。
$n=$args;(1..12|?{!+(date $n-$_).DayOfWeek}).Count
使用以下事实:如果一个月的第一天是星期天,则第13天将是星期五。
我也尝试过:
(1..12|?{!+(date $args-$_).DayOfWeek}).Count
但是$args
在脚本块内部却不一样。
$n
为$args
,您可以完全不用$n=read-host;
。保存8.拆下@,如上面提到的,和你到54
$args
的$input
,因而从管道喂养一年,并且将运行该脚本,但它总是输出3
sum(format(as.Date(paste(scan(),1:12,1,sep="-")),"%w")<1)
"%a %d")=="Fri 13"
与"%w%d)=="513")
使用陶氏的数和删除的空间。
seq
一个月实际上在这里要短一些!sum(format(as.Date(paste(scan(),1:12,13,sep="-")),"%w%d")=="513")
只有65个字符!
<
会把字符强制为整数。好招!
from datetime import*
s=c=0
exec's+=1;c+=date(%d,s,9).weekday()<1;'%input()*12
print c
9月9日星期一的铃声可能不太一样,但效果也一样。
编辑:一年半,注意到date
比datetime
:) 短
from datetime import*
f=lambda y:sum([date(y,m,13).weekday()==4 for m in range(1,13)])
.... 但是,与导入(86字节)的大小相同的解决方案。
不使用任何库或内置日期函数:
~..({4/.25/.4/--@}2*2*\3*+-
14%' [3/=RI[)a%:*.'\=5%
' [3/=RI[)a%:*.'
可能是'feefefgeeffgfe'
本质上是相同的算法。
l=lambda y:y/4-y/100+y/400
i=input()
print"21232211321211"[(2*i+3*l(i)-l(i-1))%14]
使用此技巧,可以进一步深入研究:
l=lambda y:y/4-y/100+y/400
i=input()
print 94067430>>(4*i+6*l(i)-2*l(i-1))%28&3
这利用了以下事实:在压延机方面,只有14个不同的年份,这些年份可以通过它们的最后一天以及它们是否在飞跃来区分。
l
计算到其参数为止的of年数(如果公历向后扩展到1年)。(2*i+3*l(i)-l(i-1))%14
是的缩写l(i)-l(i-1)+(i+l(i))%7*2
,它l(i)-l(i-1)
告诉我们参数是否为a年,并i+l(i)
总结最后一天的变化(正常年份中的一个,one年中的两个)。
main(int x,char**v){char p[400],*a[]={"abbababbacaacbac","bacabbb","baabbaca","abbb","aabbacaac","abbbbcaac","abbbbaabb"},*b="adcadcadcaebcadcadcafbcadcadcagbcadcadcadc";int c=0,i,y=atoi(v[0]);for(i=0;i<42;i++)strcpy(&p[c],a[b[i]-'a']),c+=strlen(a[b[i]-'a']);printf("%d",p[y%400]-'`');}
不是最短的答案,但不使用任何库。
static char GetNumberOfFriday13s(int year) { const string perpetualCalendar = "1221212213113213122221122131122121221311321312222112213112212122131132131222211221311221212213113213112213113213122221122131122121221311321312222112213112212122131132131222211221311221212213113213122213113213122221122131122121221311321312222112213112212122131132131222211221311221212213113213122221122213122221122131122121221311321312222112213112212122131132131222211221311221212213113213122221122131"; return perpetualCalendar[year % 400];
。不会负数年。
v[0]
应该是v[1]
。您也可以打高尔夫球。考虑使用strcat
,存储字符以直接在中打印a[]
,并减去数字常量而不是字符常量。:)
main(int x,char**v){char p[400],*a[]={"1221212213113213","2131222","21122131","1222","112213113","122223113","122221122"},*b="adcadcadcaebcadcadcafbcadcadcagbcadcadcadc";*p=0;for(;*b;b++)strcat(p,a[*b-97]);putchar(p[atoi(v[1])%400]);}
(215个字符)
我很惊讶地发现,只有另一种解决方案不使用内置日历工具。这也是一种(高度混淆的)数学方法,同样在C语言中:
f(x){return(x+(x+3)/4-(x+99)/100+!!x)%7;}main(int x,char**v){int y=atoi(v[1])%400,a=f(y+1);putchar('1'+((f(y)&3)==1)+(a>2&&a-5));}
(以上内容在GCC中编译,没有错误)
我更喜欢Williham Totland的解决方案以及他对压缩的使用。我修复了两个小错误,并调整了代码以缩短其长度:
main(int x,char**v){char p[400],*a[]={"1221212213113","213122221122131","12213113","22213113","22221122","2131"},*b="abababafcbababafdbababafebababab";*p=0;for(;*b;b++)strcat(p,a[*b-97]);putchar(p[atoi(v[1])%400]);}
<?for($i=1,$c=0;$i<13;$i++)$c+=(date("N",mktime(0,0,0,$i,1,$argv[1]))==7);echo $c;
基于
“从星期日开始的任何月份都包含第13个星期五,并且每个日历年至少有一个星期五在第13个星期五。”
seq -f$1-%g-6 12|date -f-|grep -c ^F
感谢@DigitalTrauma使用seq
默认的start到保存10个字符1
。
date -f<(printf "%s\n" $1-{1..12}-6)|grep -c ^F
(使用以前的版本echo
,因为空行的存在错误时<(echo $1-{1..12}-6$'\n')
,所以此功能工作的罚款,直至今天是星期五)使用。
set -- 2013
seq -f$1-%g-6 1 12|date -f-|grep -c ^F
2
date -f<(printf "%s\n" $1-{1..12}-13)|grep -c ^F
2
是取决于语言环境的,因为它不起作用,您可能必须
export LANG=C
要么
LANG=C date -f<(printf "%s\n" $1-{1..12}-13)|grep -c ^F
f(){ seq -f$1-%g-6 12|date -f-|grep -c ^F;}
f 2013
2
for i in {2010..2017};do echo $i $(f $i) ;done
2010 1
2011 1
2012 3
2013 2
2014 1
2015 3
2016 1
2017 2
从那里开始,如果我的功能变为:
f(){ o=();for t in $(seq -f$1-%g-6 12|date -f- +%a,%b);do [ "${t:0:1}" = "F" ]&&o+=(${t#*,});done;echo ${#o[@]} ${o[@]};}
要么
f(){ o=();
for t in $(seq -f$1-%g-6 1 12|date -f- +%a,%b);do
[ "${t:0:1}" = "F" ]&&o+=(${t#*,})
done
echo ${#o[@]} ${o[@]}
}
for i in {2010..2017};do echo $i $(f $i) ;done
2010 1 Aug
2011 1 May
2012 3 Jan Apr Jul
2013 2 Sep Dec
2014 1 Jun
2015 3 Feb Mar Nov
2016 1 May
2017 2 Jan Oct
C
。但是有一个错误……
%s\\n
seq
丢弃8个字符:date -f<(seq -f$1-%g-6 1 12)|grep -c ^F
seq -f$1-%g-6 12|date -f-|grep -c ^F
f=function(a){b=0;for(c=12;c--;)b+=!new Date(a,c,1).getDay();return b}
,b,c
由函数声明(!它的确定泄漏瓦尔高尔夫),也为b
被投作为一个Number
可以+=
测试的结果,而不是&&b++
:b+=/^F/.test(new Date(a,c,6))
。但是,您可以使用保存另一个字节!new Date(a,c,1).getDay()
(这是有效的,因为getDay
返回0代表星期日,如果13号是星期五,则1号将是星期日),而不是test
总共可以节省7个字节!
64个字符
{+/6={x-7*x div 7}(.:')x,/:(".",'"0"^-2$'$:1+!:12),\:".13"}[0:0]
从stdin读取
int f(int y){int c=0;for(int i=1;i<13;i++)c+=new DateTime(y,i,8).DayOfWeek>0?0:1;return c;}
int g(int y){return Enumerable.Range(1,12).Count(m=>new DateTime(y,m,8).DayOfWeek==0);}
感谢捷普·斯蒂格·尼尔森(Jeppe Stig Nielsen)的来信,并建议8日检查星期日。
Thanks to Danko Durbić for suggesting >
instead of ==
.
c+=(int)new DateTime(y,i,13).DayOfWeek==5?1:0;
, use the equivalent c+=new DateTime(y,i,8).DayOfWeek==0?1:0;
. The trick is to subtract 5
, because then you can get rid of the cast to int
, and also the number 8
has one digit less than the number 13
. Sunday the Eighth!
int g(int y){return Enumerable.Range(1,12).Count(m=>new DateTime(y,m,8).DayOfWeek==0);}
. Of course as a lambda this is y=>Enumerable.Range(1,12).Count(m=>new DateTime(y,m,8).DayOfWeek==0)
.
.DayOfWeek<1
.
c#
answer but not sure how to apply it to linq
.
DayOfWeek
with any other integer than 0
- error CS0019: Operator '<' cannot be applied to operands of type 'System.DayOfWeek' and 'int'
.
for(;++$i<13;)$c+=!date(w,strtotime($argn.-$i));echo$c;
Run with echo <year> | php -nR '<code>'
.
Basically the same that Oleg tried and Damir Kasipovic did, just with better golfing:
Every month that starts with a sunday, has a Friday the 13th.
So I loop through the months and count the first days that are sundays.
breakdown
for(;++$i<13;) // loop $i from 1 to 12
$c+=! // 4. if result is not truthy (weekday==0), increment $c
date(w, // 3. get weekday (0 stands for Sunday)
strtotime( // 2. convert to timestamp (midnight 1st day of the month)
$argn.-$i // 1. concatenate year, "-" and month
)
)
;
echo$c; // output
f: 0 repeat m 12[d: do ajoin["6-"m"-"y]if d/weekday = 5[++ f]]f
Usage example in Rebol console:
>> y: 2012
== 2012
>> f: 0 repeat m 12[d: do ajoin["6-"m"-"y]if d/weekday = 5[++ f]]f
== 3
Alternative solution which collects all the Friday 13th in given year is:
>> collect[repeat m 12[d: do ajoin["13-"m"-"y]if d/weekday = 5[keep d]]]
== [13-Jan-2012 13-Apr-2012 13-Jul-2012]
ncal $1|sed '/F/s/13/\
/g'|grep -c ^\ 2
ncal
prints a calendar for the given year with days of the week down the left.
sed
with a /g
flag subs out all 13s with newlines
grep -c
counts the lines that start with " 2" (20 always follows 13)
Thanks to @DigitalTrauma for finding a bug in my old version and proposing a solution!
ncal $1|sed /F/s/13/\\n/g|grep -c ^\ 2
In 78 chars:
def f(y:Int)=0 to 11 count(new java.util.GregorianCalendar(y,_,6).get(7)==6)
Nothing out of ordinary, except for using magical numbers for DAY_OF_WEEK = 7
and FRIDAY = 6
.
68 character version:
def f(y:Int)=0 to 11 count(new java.util.Date(y-1900,_,6).getDay==5)
Yes, Java changed values of day of the week constants between API's.
new java.util.GregorianCalendar
has to be so long :(
Works only for previous years, because monthdatescalendar
returns a calendar for the given year until now. I think there is a lot of optimizing potential left :).
import calendar, sys
c=calendar.Calendar()
f=0
for m in range(1,12):
for w in c.monthdatescalendar(int(sys.argv[1]),m):
for d in w:
if d.weekday() == 4 and d.day == 13:
f=f+1
print(f)
Another solution, works for every date but it isn't smaller:
import datetime,sys
y=int(sys.argv[1])
n=datetime.date
f=n(y,1,1)
l=n(y,12,31)
i=0
for o in range(f.toordinal(), l.toordinal()):
d=f.fromordinal(o)
if d.day == 13 and d.weekday() == 4:
i=i+1
print(i)
With the idea of not looking for 13th
but first, and as sunday
is 0
this let save 3 chars! Thanks @ Iszi and Danko Durbić!
$==$_;$_=grep{!strftime"%w",0,0,0,1,$_,$=-1900}(0..11)
Could compute 2010 to 2017 (for sample) in this way:
perl -MPOSIX -pE '$==$_;$_=grep{!strftime"%w",0,0,0,1,$_,$=-1900}(0..11)' <(
printf "%s\n" {2010..2017})
11321312
(Ok, there is no newline, but that was not asked;)
$==grep{5==strftime"%w",0,0,0,13,$_,$ARGV[0]-1900}(0..11);say$=
for i in {2010..2017};do
echo $i $(
perl -MPOSIX -E '
$==grep{5==strftime"%w",0,0,0,13,$_,$ARGV[0]-1900}(0..11);say$=
' $i );
done
2010 1
2011 1
2012 3
2013 2
2014 1
2015 3
2016 1
2017 2
In Smalltalk (Squeak/Pharo flavour), implement this method in Integer (86 chars)
countFriday13^(1to:12)count:[:m|(Date year:self month:m day:13)dayOfWeekName='Friday']
Then use it like this: 2014countFriday13
.
Of course, we could use a shorter name, but then it would not be Smalltalk
I tried a solution which doesn't make use of any date libraries.
I found a pretty cool (if I may say so myself) solution. Unfortunately I can't get it shorter than this, which really bugs me because it feels like there should be a better way.
The solution hinges on this algorithm which is only 44 bytes in itself. Unfortunately I need another 100 bytes to wrap it nicely...
#include<stdlib.h>
main(int,char**v){int f=0,d,m,y;for(m=1;m<13;++m)d=13,y=atoi(v[1]),(d+=m<3?y--:y-2,23*m/9+d+4+y/4-y/100+y/400)%7-5||++f;return f;}
Output through the return code (in C++, using cout
or printf
or anything like that requires another #include
, which would blow up the solution even more).
Driver / test program:
# Make sure we're not running an old version
rm doomsday.exe
gcc doomsday.cpp -o doomsday.exe
./doomsday.exe 1776
echo 1766: $?
./doomsday.exe 2012
echo 2013: $?
./doomsday.exe 2013
echo 2013: $?
./doomsday.exe 2014
echo 2014: $?
echo `wc doomsday.cpp -c` characters
Output of the driver program:
$ ./test_doomsday
1766: 2
2013: 3
2013: 2
2014: 1
150 doomsday.cpp characters
($m<3?$y--:$y-2)+3
instead of d=13,
, d+=m<3?y--:y-2,
and d+4
should work as well and saves a lot. +5
instead of +3
and -5
should work too and saves 2 bytes. for(m=0;++m<13;)
saves one byte. Moving m=0
to the function head saves another byte; and moving ()%7||++f
to the loop head saves another one. Down from 149 to 136 bytes.
-20 bytes by getting rid of the import
, and some whitespace I missed.
(import '[java.time LocalDate DayOfWeek])#(loop[d(LocalDate/parse(str %"-01-01"))c 0](if(=(.getYear d)%)(recur(.plusDays d 1)(if(and(=(.getDayOfMonth d)13)(= (.getDayOfWeek d) DayOfWeek/FRIDAY))(inc c)c))c))
Starting on Janauary 1st of the given year, it loops over each day. If the day is Friday the 13th, it increments the count. It continues to loop until it reaches the next year.
(import '[java.time LocalDate DayOfWeek])
(defn count-friday-13ths [year]
(loop [d (LocalDate/parse (str year "-01-01")) ; Starting date
c 0] ; The count
(if (= (.getYear d) year) ; If we haven't moved onto the next year...
(recur (.plusDays d 1) ; Add a day...
(if (and (= (.getDayOfMonth d) 13) ; And increment the count if it's Friday the 13th
(= (.getDayOfWeek d) DayOfWeek/FRIDAY))
(inc c) c))
c))) ; Return the count once we've started the next year.
echo 0x5da5aa76d7699a>>(($y=$argn%400)+($y>102?$y>198?$y>299?48:32:16:0))%28*2&3;
Run with echo <year> | php -nR '<code>'
.
breakdown
Weekdays repeat every 400 years.
In the results for 1600 to 1999 (for example), there is a 28-length period with just three gaps:
0:2212122131132131222211221311
28:2212122131132131222211221311
56:2212122131132131222211221311
84:2212122131132131122
103: 131132131222211221311
124:2212122131132131222211221311
152:2212122131132131222211221311
180:2212122131132131222
199: 131132131222211221311
220:2212122131132131222211221311
248:2212122131132131222211221311
276:221212213113213122221122
300: 2131222211221311
316:2212122131132131222211221311
344:2212122131132131222211221311
372:2212122131132131222211221311
After adjusting the year for these gaps, we can get the result with a simple hash:
$y=$argn%400;foreach([300,199,103]as$k)$y<$k?:$y+=16;
echo"2212122131132131222211221311"[$y%28];
Not short (95 bytes) but pretty. And we can golf
for(;++$m<13;23*$m/9+($m<3?$y--:$y-2)+5+$y/4-$y/100+$y/400)%7?:$f++)$y=$argn;echo$f;