How many of you that still use your own knuckle to determine whether a month is having a full 31 days or less?
You job is to write a program to count how many months, in a month range, are having a full 31 days and how many are having less than 31 days by "counting the knuckles".
Counting days of month by knuckles
Courtesy: amsi.org.au
Input
A pair of months, the first of which doesn't have to come chronologically before the second, given in any suitable format. For instance: 201703 201902 — March 2017 to February 2019. Please describe the input format you choose. Note that the input must be able to include all years from 1 to 9999. The range of months specified includes both the starting and ending months.
Output
Two integers: the number of months in the given range with 31 days and the number of months in the range with less than 31 days.
Example: 14 10 — 14 knuckles, 10 grooves (it means that in that month range we have 14 months that have a full 31 days, and 10 months that have less than 31 days).
For an input where the second month in the range comes chronologically before the first, for example 201612 201611, you have to output a pair of zero.
Examples of input and output
| Input | Output |
|---------------|-------------|
| 201703 201902 | 14 10 |
| 201701 202008 | 26 18 |
| 000101 999912 | 69993 49995 |
| 201802 201803 | 1 1 |
| 201601 201601 | 1 0 |
| 201612 201611 | 0 0 |
Rules
- You may choose any language you like
- One input per line
- This is code-golf, so shortest code in bytes wins!
- The winner will be chosen in April 9
- Standard loopholes apply
- PS: this is my first question in PCG, it might have some inconsistencies. Feel free to edit and confirm what's unclear for you.
11 Answers 11
Jelly, 21 bytes
×ばつ=Ṣ$
Takes input like [[y, m], [y, m]].
How it works
×ばつ=Ṣ$ Main link. Argument: [[a, b], [c, d]]
ḅ12 Unbase 12; yield [x, y] := [ 12a + b, 12c + d].
r/ Reduce by range; yield [x, ..., y].
¤ Combine the five links to the left into a niladic chain.
7 Set the return value to 7.
R Range; yield [1, 2, 3, 4, 5, 6, 7].
Ḃ Bit; yield [1, 0, 1, 0, 1, 0, 1].
ṁ12 Mold 12; repeat the Booleans to create an array of length
12. Yields [1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1].
ị At-index; yield the elements of the array to the right at
the indices (1-based and modular) of the array to the left.
$ Combine the two links to the left into a monadic chain.
C Complement; map t -> 1-t over the array.
ż Zip the original array with the complements.
S Take the sum of each column.
$ Combine the two links to the left into a monadic chain.
Ṣ Sort [[a, b], [c, d]].
= Compare the result with [[a, b], [c, d]], yielding 1 if
the input is sorted, 0 if not.
×ばつ Multiply the results to both sides.
JavaScript (ES6), (削除) 70 (削除ここまで) (削除) 68 (削除ここまで) (削除) 67 (削除ここまで) 64 bytes
Takes input as two integers in yyyymm format, in currying syntax (a)(b). Outputs an array of two integers [knuckles, grooves].
a=>g=(b,c=d=0)=>a>b?[c,d-c]:g(--b,c+!((b%=100)>11||b/.87&!!++d))
Formatted and commented
a => // main function: takes start date (a) as input / returns g
g = ( // recursive function g, which takes:
b, // - b = end date
c = d = 0 // - c = number of knuckles
) => // and also keeps track of: d = total number of months
a > b ? // if a is greater than b:
[ c, d - c ] // stop recursion and return the final result
: // else:
g( // do a recursive call to g():
--b, // - decrement the end date
c + // - increment the # of knuckles if
!( // both of these conditions are false:
(b %= 100) // - the end month (now stored in b in 0-based indexing)
> 11 || // is greater than 11
b / 0.87 & !!++d // - the number of days in this month is not 31
) // (at the same time, d is incremented if the first
) // condition is false)
Test cases
NB: The third test case is not included in this snippet, because it won't work unless your browser has Tail Call Optimization enabled.
let f =
a=>g=(b,c=d=0)=>a>b?[c,d-c]:g(--b,c+!((b%=100)>11||b/.87&!!++d))
console.log(f(201703)(201902)); // 14 10
console.log(f(201701)(202008)); // 26 18
console.log(f(201802)(201803)); // 1 1
console.log(f(201601)(201601)); // 1 0
console.log(f(201612)(201611)); // 0 0
Python 2, (削除) 92 (削除ここまで) (削除) 90 (削除ここまで) (削除) 86 (削除ここまで) 80 bytes
lambda a,b,c,d:[(bin(2741)[2:]*(c+1-a))[b-1:-12+d or None].count(x)for x in'10']
6 more by converting to a lambda, with thanks to @math_junkie for the idea. Now outputs a list containing the two numbers.
Previous non-lambda version (86 bytes)
a,b,c,d=input()
for x in'10':print(bin(2741)[2:]*(c+1-a))[b-1:-12+d or None].count(x),
2 saved with thanks to @ovs for helping me get rid of the len(k). I hadn't thought about using None.
Input is a list of integers in the format y1,m1,y2,m2
Some credit due to @KeerthanaPrabhakaran who got bin(2741)[2:] before I did which saves 1 byte over hard coding the binary string.
-
\$\begingroup\$ Impressive... k=bin(2741)[2:]*(c+1-a) blew my mind \$\endgroup\$0xffcourse– 0xffcourse2017年03月30日 11:11:55 +00:00Commented Mar 30, 2017 at 11:11
-
1
-
\$\begingroup\$ Brilliant! I was trying to work out hoe to get rid of the
len(k)from the last slice. Thanks. \$\endgroup\$ElPedro– ElPedro2017年03月30日 14:16:53 +00:00Commented Mar 30, 2017 at 14:16 -
1\$\begingroup\$ You can save 6 bytes by using a lambda: TIO \$\endgroup\$math junkie– math junkie2017年03月30日 15:17:43 +00:00Commented Mar 30, 2017 at 15:17
PHP, (削除) 259 (削除ここまで) (削除) 256 (削除ここまで) (削除) 249 (削除ここまで) (削除) 248 (削除ここまで) (削除) 237 (削除ここまで) 221 bytes
Outgolfed by aross: https://codegolf.stackexchange.com/a/114512/38505
Input Format: yyyymm,yyyymm
$i=explode(",",fgets(STDIN));$_="DateTime::createFromFormat";foreach(new DatePeriod($_(Ym,$i[0]),new DateInterval(P1M),$_(Ym,$i[1])) as$f)date(t,mktime(0,0,0,$f->format(m),1,$f->format(y)))>30?++$x:++$y;
echo $x.' '.++$y;
Older versions
$i=explode(",",fgets(STDIN));$_="DateTime::createFromFormat";$y=1;foreach(new DatePeriod($_("Ym",$i[0]),new DateInterval('P1M'),$_("Ym",$i[1])) as$f)date("t",mktime(0,0,0,$f->format("m"),1,$f->format("y")))==31?++$x:++$y;
echo $x.' '.$y;
$i=explode(",",fgets(STDIN));$_="DateTime::createFromFormat";$y=1;foreach(new DatePeriod($_(Ym,$i[0]),DateInterval::createFromDateString('1 month'),$_(Ym,$i[1])) as$f)date(t,mktime(0,0,0,$f->format(m),1,$f->format(y)))>30?++$x:++$y;
echo $x.' '.$y;
$i=explode(",",fgets(STDIN));$_="DateTime::createFromFormat";$y=1;foreach(new DatePeriod($_(Ym,$i[0]),DateInterval::createFromDateString('1 month'),$_(Ym,$i[1])) as$f)date(t,mktime(0,0,0,$f->format(m),1,$f->format(y)))==31?++$x:++$y;
echo $x.' '.$y;
$i=explode(",",fgets(STDIN));$_="DateTime::createFromFormat";foreach(new DatePeriod($_("Ym",$i[0]),DateInterval::createFromDateString('1 month'),$_("Ym",$i[1])) as$f)date("t",mktime(0,0,0,$f->format("m"),1,$f->format("y")))==31?++$x:++$y;
echo $x.' '.++$y;
$i=explode(",",fgets(STDIN));$_="DateTime::createFromFormat";$y=1;foreach(new DatePeriod($_("Ym",$i[0]),DateInterval::createFromDateString('1 month'),$_("Ym",$i[1])) as$f)date("t",mktime(0,0,0,$f->format("m"),1,$f->format("y")))==31?++$x:++$y;
echo $x.' '.$y;
Batch, 93 bytes
@set/ag=(y=%2/100-%1/100)*5+(x=%2%%100+6)*5/12-(w=%1%%100+5)*5/12,k=y*12+x-w-g
@echo %k% %g%
Accepts two parameters in ymm format (i.e. 101 - 999912). Previous 129-byte loop-based solution:
@set/al=%1-%1/100*88,u=%2-%2/100*88,k=g=0
@for /l %%i in (%l%,1,%u%)do @set/a"m=%%i%%12,k+=1451>>m&1,g+=2644>>m&1
@echo %k% %g%
-
\$\begingroup\$ Incorrect result for 000101 999912, probably because of the integer size limit? \$\endgroup\$0xffcourse– 0xffcourse2017年03月30日 09:41:30 +00:00Commented Mar 30, 2017 at 9:41
-
1\$\begingroup\$ @officialaimm Wrong input format, sorry - year should not have leading zeros. \$\endgroup\$Neil– Neil2017年03月30日 10:18:54 +00:00Commented Mar 30, 2017 at 10:18
Python 3.5 ((削除) 164 (削除ここまで) (削除) 162 (削除ここまで) (削除) 154 (削除ここまで) (削除) 152 (削除ここまで) (削除) 150 (削除ここまで) (削除) 148 (削除ここまで) (削除) 140 (削除ここまで) 137 bytes)
n=int;a,b=input().split();t=k=0
for r in range(n(a[4:]),(n(b[:4])-n(a[:4]))*12+n(b[4:])+1):t+=1;k+=n('101010110101'[r%12-1])
print(k,t-k)
repl.it
takes input in the form of yyyymm yyyymm
prints output as number_of_knuckles number_of_grooves
- saved 2 bytes: Thanks to Cole
- saved 8 bytes: removed unwanted variables
- saved 2 bytes: reduced t=0;k=0 as t=k=0
- saved 2 bytes: Thanks to Cole (I had missed this before)
- saved 2 bytes: Thanks to Keerthana
- saved 8 bytes: removed unwanted variables
- saved 3 bytes: Thanks to math_junkie ( split(' ') to split() )
-
1\$\begingroup\$ I think you can reduce some bytes by doing
n=intand also perhaps someexectomfoolery. \$\endgroup\$cole– cole2017年03月30日 07:03:33 +00:00Commented Mar 30, 2017 at 7:03 -
1\$\begingroup\$ I think you can do
2773&1<<r%12-1>0instead ofint('101010110101'[r%12-1])\$\endgroup\$xenia– xenia2017年03月30日 07:53:51 +00:00Commented Mar 30, 2017 at 7:53 -
\$\begingroup\$ @Loovjo I am getting error in doing so! \$\endgroup\$0xffcourse– 0xffcourse2017年03月30日 08:06:00 +00:00Commented Mar 30, 2017 at 8:06
-
1\$\begingroup\$ using
print([k,t-k])asprint(k,t-k)would produce the desired result as(k,g)thereby reducing 2 bytes! \$\endgroup\$Keerthana Prabhakaran– Keerthana Prabhakaran2017年03月30日 09:12:40 +00:00Commented Mar 30, 2017 at 9:12 -
1\$\begingroup\$ I believe you can replace
split(' ')withsplit()\$\endgroup\$math junkie– math junkie2017年03月30日 13:40:02 +00:00Commented Mar 30, 2017 at 13:40
Python 2, (削除) 147 (削除ここまで) (削除) 146 (削除ここまで) 142 bytes
def s(a,b):y=100;r=bin(2741)[2:];x=b/y-a/y;i=r*(x-1);return((0,0),map((i+r[a%y-1:]+r[:b%y]if i or x else r[a%y-1:b%y]).count,('1','0')))[a<=b]
- Saved 4 bytes - Thanks to @math_junkie for suggesting the if-else clause with array lookup!
Breaking down the code,
def s(a,b):
y=100
r=bin(2741)[2:] #'101010110101'
x=b/y-a/y #to get the difference between the two years
i=r*(x-1)
return((0,0),map((i+r[a%y-1:]+r[:b%y]if i or x else r[a%y-1:b%y]).count,('1','0')))[a<=b]
-
1\$\begingroup\$ You can save bytes by replacing the
if-elseclauses with array lookups. See this post for details \$\endgroup\$math junkie– math junkie2017年03月30日 15:07:52 +00:00Commented Mar 30, 2017 at 15:07 -
\$\begingroup\$ That was indeed a cool way! Didn't know that before! Thanks! \$\endgroup\$Keerthana Prabhakaran– Keerthana Prabhakaran2017年03月30日 15:22:28 +00:00Commented Mar 30, 2017 at 15:22
PHP, (削除) 120 (削除ここまで) (削除) 103 (削除ここまで) (削除) 97 (削除ここまで) 96 bytes
for($f=strtotime;$f($argv[2])>=$n=$f($argv[1].+$x++.month);)$k+=date(t,$n)>30;echo+$k,_,$x-1-$k;
Run like this:
php -nr 'for($f=strtotime;$f($argv[2])>=$n=$f($argv[1].+$x++.month);)$k+=date(t,$n)>30;echo+$k,_,$x-1-$k;' 0001-01 9999-12;echo
> 69993_49995
Explanation
for(
$f=strtotime; # Alias strtotime function which is called twice.
$f($argv[2]) >= # Create end date timestamp. Iterate until the end
# date is reached.
$n=$f(
$argv[1].+$x++.month # Create timestamp from start date + X months.
);
)
$k+=date(t,$n) > 30; # If "t" of current date (days in month) is 31
# increment $k (knuckles).
echo+$k,_,$x-1-$k; # Compute grooves (iterations - $k) and output,
# implicit cast to int to account for 0 count.
Tweaks
- Saved 17 bytes by using timestamp style instead of DateTime object style
- Saved 6 bytes by not assigning end date timestamp to variable
$e, just compare directly - Saved 1 byte by not keeping count of grooves, but just computing it after the loop
-
\$\begingroup\$
$x++instead of+$x++also works. \$\endgroup\$Titus– Titus2017年03月31日 17:24:38 +00:00Commented Mar 31, 2017 at 17:24 -
\$\begingroup\$ @Titus, I had that at first, but realized that with
$xuninitialized the string would be2017-12month, which is an unrecognized format and results in 1970 \$\endgroup\$aross– aross2017年04月03日 13:37:52 +00:00Commented Apr 3, 2017 at 13:37 -
\$\begingroup\$ Wicked ... it worked somewhere. Wicked enough though that it works without a literal
+in the string. \$\endgroup\$Titus– Titus2017年04月04日 12:33:29 +00:00Commented Apr 4, 2017 at 12:33
PowerShell, 96 bytes
for($a,$b=[datetime[]]$args;$a-le$b;$a=$a.AddMonths(1)){$x++;$z+=$a.Month-in2,4,6,9,11};$x-$z;$z
Takes input as form 2017-03. Uses the built-in .NET date libraries and loops through from inputs $a to $b, each iteration incrementing $x++ and adding to $z if the current .Month is -in 2,4,6,9,11 (i.e., a non-31-day month). Then we output our total months minus the non-31-day months $x-$z, and the non-31-day months $z.
Tosses an error on the 0001-01 to 9999-12 test case, because .NET only supports years up to 9999, so the final .AddMonths(1) causes an overflow. Still outputs the correct values, though, because it's a non-terminating error; it just causes the loop to exit.
Probably would be shorter to do this arithmetically, like the Python or JavaScript answers, but I wanted to show an approach using the .NET built-ins.
Bash, 113 bytes
s="1ドル-1";e="2ドル-1";sort <(while [ "$s" \< "$e" ];do s=$(date +%F -d"$s+1month");date +%d -d"$s-1day";done)|uniq -c
needs golfing...
takes input as 2016-03 2018-10
outputs:
1 28
7 30
12 31
ungolfed:
s="1ドル-1"
e="2ドル-1" # adds first day of month to the dates
sort <(
while [ "$s" \< "$e" ]; do #iterates over dates
s=$(date +%F -d"$s+1month") #adds one month to start date
date +%d -d"$s-1day" #outputs last day of previous month
done) | uniq -c #counts ocurrences of day number prevously sorted
Swift, 151 bytes
let f={(m:[Int])->[Int] in var k=[0,0]
(m.min()!...m.max()!).map{0ドル%100}.filter{0ドル>0&&0ドル<13}.forEach{m in let n = m>7 ?m-7:m
k[(n%2+1)%2]+=1}
return k}
input is an array of two integers in the format as per the example
201612 201611. \$\endgroup\$