22
\$\begingroup\$

Prime Wednesdays

Your task is to count the number of Wednesdays that fall on a prime day of the month in a particular year. For instance, 7-13-16 is a prime Wednesday. For consistency use the Gregorian calendar for all dates.

Input

The input to your program/function will be a year (eg 2016) and is flexible. The year will be an integer between 1912 and 2233 inclusive.

Output

The output is also flexible and should be the number of prime Wednesdays (eg 18).

Scoring

This is so shortest code in bytes wins!

Test Cases

input -> output
--------------------
1912 -> 19
1914 -> 16
1984 -> 17
1996 -> 19
2063 -> 19
2150 -> 16
2199 -> 18
2233 -> 18

\$\endgroup\$

14 Answers 14

7
\$\begingroup\$

MATL, 38 36 34 bytes

FT+"@llI$YO]q&:t8XO!s9\~)9#1$ZOZps

Try it online! Or verify all test cases (takes a few seconds).

Explanation

FT+     % Input year implicitly. Add [0 1] element-wise. Gives array with input year
        % and next year
"       % For each of those two years
  @     %   Push year
  ll    %   Push 1 twice. This indicates January 1.
  I$YO  %   Convert year, month, day to serial date number
]       % End for each. We now have the serial date number for January 1 of the input
        % year and that of the following year
q       % Subtract 1 to the latter, to yield December 31 of the input year
&:      % Inclusive range between those two numbers. This gives an array of serial date
        % numbers for the whole input year
t       % Push another copy of that array
8XO     % Convert to date string with format 8. This gives weekday as "Mon", "Tue" etc.
        % The result is a 3-column 2D char array, where each row is a day
!s      % Transpose, sum of each column. 'Wed' gives 288 (sum of ASCII codes)
9\~     % 288 gives 0 modulo 9, and is the only weekday to do so. So we compute modulo 9
        % and negate. This gives true for Wednesdays, false for the rest
)       % Apply as logical index into the array of serial date numbers
9#1$ZO  % Array of month numbers corresponding to those serial date numbers
Zp      % Array that contains true for prime numbers, false for the rest
s       % Sum of array. Display implicitly
\$\endgroup\$
  • \$\begingroup\$ I'm convinced that MATL cannot be beaten on date-based challenges. We should create DATL which is further optimized to handle date-based challenges. \$\endgroup\$ – Suever Jul 28 '16 at 21:21
  • \$\begingroup\$ @Suever Haha, nice name \$\endgroup\$ – Luis Mendo Jul 28 '16 at 21:57
20
\$\begingroup\$

Python 2, 95 93 68 67 bytes

lambda y:0x10ea2c8dbb06c5619/5**((y+((y-22)/99-y/2002)*16)%28)%5+16

Thanks to @Josay for golfing off 1 byte!

Test it on Ideone.

\$\endgroup\$
  • 3
    \$\begingroup\$ You can save 1 char with 0x10ea2c8dbb06c5619 instead of 19501370182350951961. \$\endgroup\$ – Josay Jul 28 '16 at 8:54
  • \$\begingroup\$ I understand the idea of big_constant//5**long_expression but how on Earth did you come with that constant and that expression? It's crazy :D \$\endgroup\$ – Sherlock9 Jul 28 '16 at 11:51
  • 2
    \$\begingroup\$ The constant is simple a lookup table using base 5 digits, but converted to base 10, so that the digits is extracted numerically rather than using a string index. The expression looks like a perpetual calendar to me. (The problem would be too easy if it was limited to years from 1901 to 2099, as the answers repeat every 28 years within that interval, so it would be just a case of taking the year mod 28 and looking it up in the table.) \$\endgroup\$ – Neil Jul 28 '16 at 12:38
13
\$\begingroup\$

Brain-Flak, 6588, 2310, 2308, 2290 bytes

First things first, I did not write nearly 100% of this program, which is probably evidenced by the enormous size of the program. Most of this code was written by my own Brain-Flak golfing algorithm. Along with an additional python script I wrote to prompt it in the right direction.

Try it online!

({}<(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((()()())){}{}){})[()()])())()())[()()()()])())()()())[()()()])()())[()()()])()())[()]))())()())[()()()()])())()()())[()()()])())())[()])[()])()()())[()()])()())[()()()()])()())())[()()])())()())[()()()()])()())[()])()()())[()()])()())[()()()()])()())())[()()])())()())[()()()()])())()()())[()()()])()())[()()()])()())[()]))())()())[()()()()])())()()())[()()()])())())[()])[()])()()())[()()])()())[()()()()])()())())[()()])())()())[()()()()])())()()())[()()()])()())[()()()])()())[()]))())()())[()()()()])())()()())[()()()])())())[()])[()])()()())[()()])()())[()()()()])()())())[()()])())()())[()()()()])())()()())[()()()])()())[()()()])()())[()]))())()())[()()()()])())()()())[()()()])())())[()])[()])()()())[()()])()())[()()()()])()())())[()()])())()())[()()()()])())()()())[()()()])())()())[()()()()])()())())[()()])())()())[()()()()])())()()())[()()()])()())[()()()])()())[()]))())()())[()()()()])())()()())[()()()])())())[()])[()])()()())[()()])()())[()()()()])()())())[()()])())()())[()()()()])())()()())[()()()])()())[()()()])()())[()]))())()())[()()()()])())()()())[()()()])())())[()])[()])()()())[()()])()())[()()()()])()())())[()()])())()())[()()()()])())()()())[()()()])()())[()()()])()())[()]))())()())[()()()()])())()()())[()()()])())())[()])[()])()()())[()()])()())[()()()()])()())())[()()])())()())[()()()()])())()()())[()()()])()())[()()()])()())[()]))())()())[()()()()])())()()())[()()()])())())[()])[()])()()())[()()])()())[()()()()])()())())[()()])())()())[()()()()])())()()())[()()()])()())[()()()])()())[()]))())()())[()()()()])())()()())[()()()])())())[()])[()])()()())[()()])()())[()()()()])()())())[()()])())()())[()()()()])())()()())[()()()])()())[()()()])()())[()]))())()())[()()()()])())()()())[()()()])())())[()])[()])()()())[()()])()())[()()()()])()())())[()()])())()())[()()()()])())()()())[()()()])()())[()()()])()())[()]))())()())[()()()()])())()()())>[(((((((((()()()()())){}{}){}){}){}){}[()]){}){}){}]){({}[()]<{}>)}{}({}<{{}}>)

While this program is quite long for code golf, it really is decently short for Brain-Flak. Currently the world record for integer division is over 1000 bytes.

Explanation

The algorithm is quite simple. Because there is a limited number of years available (321) it simply pushes the answers in reverse order under the input and uses a lookup algorithm to find the correct answer. While hardcoding all 321 possibilities may seem rather inefficient with an task as complex as this one and a language as esoteric as brain-flak it may very well be the best solution. (I plan to find out in the coming week).

Since most of the 321 numbers are about 18 on average and they differ by very little from year to year instead of pushing all the numbers individually I push the first year (2233) normally and then just duplicate and change the value a bit for each year after. This way instead of paying to push ~18 for all 321 years I only pay to push ~2 for every year.

Once all the answers have been pushed it subtracts 1912 from the input ({}[(((((((((()()()()())){}{}){}){}){}){}[()]){}){}){}]) (This may be suboptimal, I rewrote the optimizer to skip certain values that I believed would not be optimal because hardcoding numbers is a super-exponential process and running it to completion may have taken a few days).

It then subtracts one from the first element and pops the second element until the result reaches zero, {({}[()]<{}>)}.

It pops the zero, {} and all the elements below the top element ({}<{{}}>).

\$\endgroup\$
  • \$\begingroup\$ What's the general approach to golfing numbers? \$\endgroup\$ – Neil Jul 28 '16 at 12:34
  • \$\begingroup\$ The simple idea is if you have a number with factors n and m you push n m-1 times and then pop m-1 times. The initial push evaluates as n and each pop evaluates as an additional n, making for (1+m-1)(n) which is the same as mn. This is done recursively because in order to push n we have to golf n as well. Since this method doesn't work well for some numbers particularly primes, we also look around to see if there is more efficient number nearby and if so we express this as the sum of that number and the difference. \$\endgroup\$ – Sriotchilism O'Zaic Jul 28 '16 at 12:41
  • \$\begingroup\$ I see... so given two numbers n and m which have lengths k and l, I assume n+m would have length k+l? What about n*m? \$\endgroup\$ – Neil Jul 28 '16 at 18:37
  • \$\begingroup\$ n*m would be k+4m-4 or l+4n-4. This is because the multiplication is hardcoded. We first push n m-1 times. In order to do this we need k symbols in order to express n and 2m-2 symbols to express the pushes (each push is 2 symbols). Then we pop m-1 times, costing us an additional 2m-2 (pops cost 2 symbols as well). This totals to k+4m-4. we can also multiply m*n (commutative property) to get l+4n-4. The result will be the shorter of the two. \$\endgroup\$ – Sriotchilism O'Zaic Jul 28 '16 at 19:01
  • 1
    \$\begingroup\$ Well, if that's true, then +1 costs 2, *2 costs 4, *3 costs 8, *4 costs 12, which is more expensive than *2*2, so not worth it (out of numbers below 1000 I only found 10 that didn't use *2: 1, 2, 3, 4, 5, 9, 15, 27, 45, 135). For 1912 the best I could do was ((((((1+1+1)*2+1)*2*2+1)*2+1)*2+1)*2+1)*2*2*2 with a length of 52. \$\endgroup\$ – Neil Jul 28 '16 at 23:54
7
\$\begingroup\$

Bash + common utilities, 39

ncal $1|grep W|factor|egrep -c ': \S+$'

Takes the input year as a command-line parameter. Typically outputs messages like this to STDERR - I think this is legal as per this meta-answer:

factor: ‘We’ is not a valid positive integer

If you want to explicitly suppress the STDERR output then you can do this instead for a score of 43:

ncal $1|grep W|factor 2>-|egrep -c ': \S+$'
\$\endgroup\$
  • \$\begingroup\$ Note that this assumes an English or C/POSIX locale. It doesn't work so well in gd_GB.utf8, where all the day names abbreviate to Di. \$\endgroup\$ – Toby Speight Aug 1 '16 at 15:14
6
\$\begingroup\$

Octave, 86 bytes

This is not fast, by any stretch. But that's not really the goal of a code golf, is it?

function r=p(y)r=0;for(i=698346:7:815953)d=datevec(i);r+=d(1)==y*isprime(d(3));end;end

Octave can track dates by "date number" - number of days elapsed where Jan 1, 0 is day 1. By this measure, Jan 3, 1912 (the first Wednesday in our set) is day 698,346. Start there, and iterate through every 7th day (all Wednesdays) until the end of 2233, and add 1 if the year is the target year AND the month-day is prime.

\$\endgroup\$
5
\$\begingroup\$

Python 2.7, 166, 165, 150 bytes

from datetime import*
y=input()
d,c=date(y,1,1),0
while d.year==y:n=d.day;c+=n>1<2==d.weekday()>0<all(n%x for x in range(2,n));d+=timedelta(1)
print c

There is certainly room for improvement here. I am rather new to golfing in python. This uses the datetime module. It loops through all the days in the year adding one to an accumulator if it fits the criterion. It then prints the result. Most of the heavy lifting is in the module so the code can be quite slim.

One byte saved thanks to Morgan Thrapp and 15 bytes saved by Pietu1998.

\$\endgroup\$
  • 1
    \$\begingroup\$ You can save one byte by switching n%x==0 to n%x<1. \$\endgroup\$ – Morgan Thrapp Jul 28 '16 at 13:41
  • 2
    \$\begingroup\$ The -1 is not necessary as range's end index is exclusive. Additionally, you can convert the filter to a generator. [0for x in range(2,n)if n%x<1] \$\endgroup\$ – PurkkaKoodari Jul 28 '16 at 14:02
  • \$\begingroup\$ You could use any(...) or all(...) instead of not filter(...). \$\endgroup\$ – kennytm Jul 28 '16 at 14:05
  • 1
    \$\begingroup\$ By combining chained comparisons and all you can save a whole bunch. c+=n>1<2==d.weekday()>0<all(n%x for x in range(2,n)) \$\endgroup\$ – PurkkaKoodari Jul 28 '16 at 14:10
3
\$\begingroup\$

J, 44 bytes

+/@(valdate*3=weekday)@,.&(,/(>:,"0/p:)i.12)

I just discovered that J has builtins for date manipulation.

Usage

Extra commands are used for formatting multiple input/output.

   f =: +/@(valdate*3=weekday)@,.&(,/(>:,"0/p:)i.12)
   (,.f"0) 1912 1914 1984 1996 2063 2150 2199 2233
1912 19
1914 16
1984 17
1996 19
2063 19
2150 16
2199 18
2233 18

Explanation

+/@(valdate*3=weekday)@,.&(,/(>:,"0/p:)i.12)  Input: year
                                       i.12   The range [0, ..., 11]
                              >:              Increment each to get the months [1, ..., 12]
                                    p:        Get the first 12 primes [2, ..., 37]
                                ,"0/          Make a table between each month and prime
                           ,/                 Join the rows
                       ,.&                    Prepend the year to each
                                              The date format is YYYY MM DD
            3=weekday                         Check if each date occurs on Wednesday
    valdate*                                  and is a valid date
+/@                                           Count the number of true values and return
\$\endgroup\$
1
\$\begingroup\$

PowerShell v3+, 99 95 bytes

Brute force approach --

param($y)(1..12|%{$m=$_;2,3,5,7,11,13,17,19,23,29,31|?{(date "$m-$_-$y").DayofWeek-eq3}}).Count

Takes input $y, loops from 1 to 12, stores the month temporarily into $m, then loops over every prime from 2 to 31. For each of those, we construct a Get-Date of that particular day, then select only those with DayOfWeek -equal to 3 (i.e., Wednesday). Encapsulates that all in a parens to formulate an array, and takes the .Count thereof.


Alternatively, mathematical approach --

PowerShell v3+, 105 bytes

param($y)(16,19,18,20,16,18,19)[($a=(date "1-1-$y").DayOfWeek)]+(1,-3,0,1,2)[$y%5]*($a-in0,2,3,4)*!($y%4)

Winds up being just a hair longer than the brute force approach, but I'm including it here since it may be beneficial to others.

Again takes input $y as the year. This time we're performing strictly math operations based on the first day of the year. We first calculate what day of the week that is, and store that into $a for use later. That indexes into the first array, which gets us the number that is usually correct. We have to add to that a second index based on whether it's a potential leap year, whether it's a Sunday, Tuesday, Wednesday, or Thursday, and based on what the year is.

This is based on the following observation. The first column is what day of week January 1st is, the second is the usual output. Unless the year is one of the middle numbers, then it's the number in parens instead. The final column describes how the %5 indexing works.

Jan-1 -> #  ... Except if $y=       (then it's this number) | $y % 5 =
Sun   -> 16 ... 1928 1956 1984 etc. (17)                    |    3
Mon   -> 19
Tue   -> 18 ... 1924 1952 1980 etc. (20)                    |    4
Wed   -> 20 ... 1936 1964 1992 etc. (17)                    |    1
Thur  -> 16 ... 1920 1948 1976 etc. (17)                    |    0
Fri   -> 18
Sat   -> 19

Note: Both of these assume en-us is the current PowerShell setting for culture/date information. The date formatting and DayOfWeek number may need to be adjusted accordingly for other culture variants.

\$\endgroup\$
1
\$\begingroup\$

Ruby, 83 + 15 (-rdate -rprime flags) = 98 bytes

Try it online! (Imported modules are inlined because idk if I can use flags in repl.it)

->y{k=0;Prime.each(31){|d|k+=(1..12).count{|m|Date.new(y,m,d).wday==3 rescue p}};k}
\$\endgroup\$
1
\$\begingroup\$

JavaScript ES6, 187 182 181 179 bytes

179 Swapped in a for-loop for the while-loop

z=y=>{D=new Date("1/3/1912");N=0;a=()=>D.getFullYear();b=()=>D.getDate();c=()=>D.setDate(b()+7);for(;a()<=y;c())N+=y-a()?0:-1<[2,3,5,7,11,13,17,19,23,29,31].indexOf(b());return N}

181 Compacted the ternary

z=y=>{D=new Date("1/3/1912");N=0;a=()=>D.getFullYear();b=()=>D.getDate();c=()=>D.setDate(b()+7);while(a()<=y){N+=y-a()?0:-1<[2,3,5,7,11,13,17,19,23,29,31].indexOf(b());c()}return N}

182 Combined the two loops

z=y=>{D=new Date("1/3/1912");N=0;a=()=>D.getFullYear();b=()=>D.getDate();c=()=>D.setDate(b()+7);while(a()<=y){N+=a()==y?-1<[2,3,5,7,11,13,17,19,23,29,31].indexOf(b()):0;c()}return N}

187

z=y=>{D=new Date("1/3/1912");N=0;a=()=>D.getFullYear();b=()=>D.getDate();c=()=>D.setDate(b()+7);while(a()<y)c();for(;a()==y;c())N+=-1<[2,3,5,7,11,13,17,19,23,29,31].indexOf(b());return N}
\$\endgroup\$
  • \$\begingroup\$ I don't think this counts as you've given the first initial Wednesday for the specific year, in this example. The OP's challenge states that it needs the year as the only parameter... Great effort so far though... \$\endgroup\$ – WallyWest Aug 1 '16 at 1:22
  • \$\begingroup\$ "The input to your program/function will be a year" -- but what you're pointing out is not that. I use the first Wednesday of 1912 as a seed because it is or is before every other Wednesday in the time period given by the OP, but I could just as easily use any arbitrary Wednesday from 1911 or before to seed it too. The input to my function is still a year, and the function still calculates the number of prime Wednesdays in any given year in the time-frame suggested by the OP, so I'm not sure how this doesn't fit the challenge. \$\endgroup\$ – Pandacoder Aug 1 '16 at 13:09
  • \$\begingroup\$ Ah, apologies... I didn't realize at first you're using that as a seeding component... Great idea... Especially considering your solution beats mine by about 30.. ;) \$\endgroup\$ – WallyWest Aug 1 '16 at 23:26
  • 1
    \$\begingroup\$ Thanks. I drew inspiration from Eamon Olive's Brain-Flak implementation, which actually has all of the answers pre-programmed, according his explanation. \$\endgroup\$ – Pandacoder Aug 1 '16 at 23:54
1
\$\begingroup\$

Batch, 248 bytes

@set/ad=0,l=1,n=20
@for /l %%i in (1913,1,%1)do @set/ad=(d+l+1)%%7,l=!(%%i%%4)-!(%%i%%100)+!(%%i%%400)
@goto %l%%d%
:03
:06
@set/an-=1
:12
:13
:16
@set/an-=1
:01
:04
:14
@set/an-=1
:00
:05
:10
:15
@set/an-=1
:02
:11
@echo %n%

Explanation: d is the day of the week, with 0 for Monday, which is conveniently 1st Jan 1912. l is a flag for whether the year is a leap year, 1 for 1912. We then loop through from 1913 to the input year, updating the day of week and recalculating the leap year flag as we go. Finally we use the leap year flag and day of week to index into what is effectively a big switch statement to determine n, the number of prime Wednesdays. Setting n to 20 and decrementing it with fall though is cheaper than using flow control logic, but the upshot is that if the 1st of January of a non-leap year is Thursday or Sunday then there are 16 prime Wednesdays and so on for the other cases.

\$\endgroup\$
1
\$\begingroup\$

JavaScript ES6 206 203 199 197 195 183 182 179

Not the shortest, but best I can do for now... Golfing suggestions welcome...

p=n=>--d-1?n%d&&p(n):1;v=Date;D=(x,y)=>new v(x.setDate(x.getDate()-y));W=a=>eval('for(Z=0,z=D(w=new v(a,11,31),(w.getDay()+4)%7);z>new v(a,0,1);)Z+=~~p(d=z.getDate()),z=D(z,7);Z')

Changes:

  1. altering ternary component from: 3>=x?3-x:10-x to 6-(x+10)%7, saving: 3 Changes to declaration locations;
  2. merged x=w.getDay();z=D(w,6-(x+10)%7) to z=D(w,6-(w.getDay()+10)%7), saving: 4
  3. shifted Z=0 from for loop to Date declaration and pushed z=D(w,6-(x+10)%7) into for loop to tidy up, saving: 2
  4. shifted w=new Date(a,Z=0,1) declaration into for loop, merging with existing w declaration, saving: 2
  5. rewriting the prime finding function to a prime testing function, saving: 12
  6. changing +!! to ~~ to reduce and still convert p(d=1) from NaN to 0, allowing Prime Test function to still work, saving: 1
  7. Moved all extra functions out of main calling function W, redefined for loop - going in reverse from 31st Dec, writing Date object out as a separate variable, then re-wrote for loop into eval call; saving 3.

@PandaCoder, I'm catching up to you, mate!

\$\endgroup\$
1
\$\begingroup\$

R, 149 147 bytes

y=function(x){s=strftime;b=ISOdate
a=seq(b(x,1,1),t=b(x,12,31),b='d')
length(a[s(a,'%u')==3&trimws(s(a,'%e'))%in%c(2,3,5,7,11,13,17,19,23,29,31)])}

Test it on Ideone.

\$\endgroup\$
0
\$\begingroup\$

Groovy, 126

Groovy doesn't have prime number validation, had to build that too.

{n->p={x->x<3||(2..Math.sqrt(x)).every{x%it}};(new Date("1/1/$n")..new Date("12/31/$n")).collect{it[7]==4&&p(it[5])?it:0}-[0]}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.