Kshitij Saraogi
Kshitij Saraogi

Reputation: 7619

Project Euler #19, Python

I was solving Project Euler #19:

How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)?

And here is the code :

months = { "January": 31,
        "February" : 28,
        "March" : 31,
        "April" : 30,
        "May" : 31,
        "June" : 30,
        "July" : 31,
        "August" : 31,
        "September" : 30,
        "October" : 31,
        "November" : 30,
        "December" : 31}

def countingSundays():
    day = 1
    sunday_count = 0

    for year in xrange(1901,2001):

        for m in months:

            day += months[m]
            if year % 4 == 0 and m == "February":
                day += 1
            if day % 7 == 0:
                sunday_count += 1

print "Sundays:", sunday_count

The output of the program is 172 which is incorrect. I searched the answer to be 171. So I wanted to know why am I getting the extra 1 Sunday ?

Upvotes: 1

Views: 13644

Answers (14)

Eric
Eric

Reputation: 24954

There are 1200 months in 100 years, 1200 / 7 = 171.4, try 171 get the correct answer.

If not, try 172 & 170, 173 & 169, ..

Upvotes: 1

Deugoué Serge
Deugoué Serge

Reputation: 1

you have initialized the day variable to 1 but the 1st Jan 1901 is a Tuesday. I made the same error ;-)

Upvotes: 0

Anirudh
Anirudh

Reputation: 1

months=[31,28,31,30,31,30,31,31,30,31,30,31]
leap=[31,29,31,30,31,30,31,31,30,31,30,31]

sundays=0
start=2
for y in range(25):
  for nonleap in range (3):
    for j in months:
      start=(start+j)%7
      if start == 0:
        sundays+=1
    
  for m in leap:
    start=(start+m)%7
    if start == 0:
      sundays+=1
print sundays

Upvotes: 0

user12033367
user12033367

Reputation:

I think I got the answer. I am not sure though.. your logic was right. But needed a little improvement. We need to start off by counting the number of Tuesdays first as we clearly know that it was Monday on Jan 1, 1900.


months = { "January": 31,
        "February" : 28,
        "March" : 31,
        "April" : 30,
        "May" : 31,
        "June" : 30,
        "July" : 31,
        "August" : 31,
        "September" : 30,
        "October" : 31,
        "November" : 30,
        "December" : 31}


for month in months:
    print(months[month])


tuesday_count = 0
day = 0 
extra_days = 0
for year in range(1901, 2001):
    days_in_the_year = 0
    for month in months:
        day += months[month]
        days_in_the_year += months[month]

        if( year % 4 == 0 and month == 'February'):
                if (year % 100 != 0):
                    extra_days += 1
                    days_in_the_year += 1
                    day += 1
                elif(year % 100 ==0 and year % 400 ==0):
                    extra_days += 1
                    days_in_the_year += 1
                    day += 1
        if( (day) % 7  == 0):
                tuesday_count += 1
    print('No. of days in the year',year,'are',days_in_the_year)

print('No. of Tuesdays counted so far is  =', tuesday_count)
print('The number of extra_days because of the leap years are:',extra_days)
# print('extra_days % 7 =', '25 % 7 =', extra_days % 7)
print('So, there were', extra_days // 7, 'extra_no_of_weeks left that we haven\'t considered. After that, it\'s followed by --wed, thu, fri and sat (we don\'t need to consider that).\n So, the total number of Tuesdays are', tuesday_count+3 )

tuesday_count += 3

print('This means only 2 Sundays that have followed')
sunday_count = tuesday_count - 1
print('Since, 1901 Jan1 would be a Tuesday, we need to subract one from the total number of Sundays\n So, the total number of sundays are:', )
sunday_count=sunday_count-1
print(sunday_count)

Upvotes: 0

Aakash Thoriya
Aakash Thoriya

Reputation: 25

==>Solution in python

euler19.py

normal_year = [31,28,31,30,31,30,31,31,30,31,30,31]
leap_year = [31,29,31,30,31,30,31,31,30,31,30,31]

years = [ normal_year ] * 100
for i in range(3, len(years), 4) :
    years[i] = leap_year

current_day = (0+365) % 7
sundays = 0
for y in years :
    for m in y :
        if current_day % 7 == 6:
            sundays += 1
        current_day += m%7
print (sundays)

Upvotes: 0

Uğur Akıncı
Uğur Akıncı

Reputation: 1

A = [31,28,31,30,31,30,31,31,30,31,30,31]
sunday =0
gK = 1
for y in range(1901,2001):
    if(y %4 ==0):
        A[1] = 29
    else:
        A[1] = 28
    for m in range(len(A)):
        for d in range(1,A[m]+1):
            if(gK ==6):
                if(d==1):
                    sunday +=1

                gK =0
            else:
                gK =gK+1
print(sunday)

Upvotes: 0

Shivam Shah
Shivam Shah

Reputation: 41

I tried the Mathematical approach although we could use the calendar functions. I first calculated the math of the months to determine the relationships between the first dates of the months using the other months. Also, for simplicity in calculating leap years, I calculated the year from March to Feb. If you want to calculate for the Jan and Feb of 1901, you can write a separate condition, and do the same to remove Jan and Feb of 2001. However, in this case, they do not really matter as they are not Sundays, so you could remove the last if condition for this specific case.

    # Zero is Sunday and the rest of the days are according to mod7
    # li stores the first days of the months in the year in every iteration
    # The year in initial li is 1900 but the contents are Mar-1900 to Feb-1901
    # At the end, we can check if Jan or Feb of 2001 contain a Sunday and remove if it does
    li, cnt = [4,0,2,5,0,3,6,1,4,6,2,5], 0 
    # Could also initialize li from by the same method as below, but I had already calculated those
    # cnt adds number of zeros in every iteration (the number of Sundays in every year) to its value
    # As we don't count for the year 1900 cnt=0, else initialize cnt=li.count(0)
    for year in range(1901,2001):
        if year%4==0:
            li[0]=li[8]=(li[11]+1)%7    #Set March and November to +1 value than last Feb
        else:
            li[0]=li[8]=li[11]          #Set March and November to same value as last Feb
        # The following values of other months will depend solely on their own March value
        # You can check the Math if you want to
        li[3]=li[11]=(li[0]+1)%7;li[6]=li[9]=(li[0]+2)%7;li[1]=li[4]=(li[0]+3)%7;li[2]=li[10]=(li[0]-2)%7;li[5]=(li[0]-1)%7;li[7]=(li[0]-3)%7
        cnt = cnt + li.count(0)
    # This is to remove the extra two months of the year 2001 if they bother the answer
    if li[10] == 0 or li[11] == 0:
        cnt = cnt-1
    print(cnt)

This was my first answer on StackOverflow, I hope I wrote well. ;-)

Upvotes: 1

Edmond Mbadu
Edmond Mbadu

Reputation: 11

Here is a different approach to tackle this question

public static void main(String[] args) {

    int k = 0;
    // String months[] = { "January", "February", "March", "April", "May", "June",
    // "July", "August", "September",
    // "October", "November", "December" };
    String Days[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
    int MonthsDdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    int counter = 0;
    for (int t = 1900; t <= 2000; t++) {
        MonthsDdays[1]=28;
        if (t % 4 == 0) {
            if (t % 100 == 0)

            {
                if (t % 400 == 0)
                    MonthsDdays[1] = 29;
            } else if (t % 100 != 0)
                MonthsDdays[1] = 29;

        }

        int p = 0;
        while (p < 12) {

            for (int j = 0; j < MonthsDdays[p]; k++, j++) {
                if (k == 7)
                    k = 0;
                if (Days[k].equalsIgnoreCase("Sunday") && j == 0 && t > 1900) {
                    counter++;

                }
            }
            p++;
        }
    }
    System.out.println(counter);
}

Upvotes: 1

morosoph
morosoph

Reputation: 1

So I approached this problem from not a date perspective but of a counting days. Here's my solution:.

days_1st = list()
day_counter = 1
for year in range(1900, 2001):
    for month in range(1,13):
        #Skip for year 1900 as count starts from 1901, but this still 
        #adds the days hence keeping the cycle in sync!
        if year != 1900: 
            days_1st.append(day_counter)
        if month == 4 or month == 6 or month == 9 or month == 11:
            day_counter+=30
        elif month == 2 and ((year % 100 == 0 and year % 400 == 0) or (year % 100 != 0 and year % 4 == 0)):
            day_counter+=29
        elif month == 2:
            day_counter+=28
        else:
            day_counter+=31
# mod 7 because since the day the counting started (1 Jan 1900 - 
# Monday) Every 7th day is a sunday!
days_sunday = list(filter(lambda x: x % 7 == 0, days_1st))
print(len(days_sunday)) 

Upvotes: 0

rodriguesra
rodriguesra

Reputation: 47

import pandas as pd
from datetime import date


start = date(1901, 1, 1)
end = date(2000, 12, 31)
d = pd.date_range(start, end, freq='MS').strftime('%A')
s = pd.Series(d)

print(s.value_counts())

Upvotes: 0

user5106014
user5106014

Reputation:

import time
from math import floor

"""
Gaussian algorithm to determine day of week
"""
def day_of_week(year, month, day):
    """
    w = (d+floor(2.6*m-0.2)+y+floor(y/4)+floor(c/4)-2*c) mod 7
    Y = year - 1 for January or February
    Y = year for other months
    d = day (1 to 31)
    m = shifted month (March = 1, February = 12)
    y = last two digits of Y
    c = first two digits of Y
    w = day of week (Sunday = 0, Saturday = 6)
    """

d = day
m = (month - 3) % 12 + 1
if m &gt; 10: Y = year - 1
else: Y = year
y = Y % 100
c = (Y - (Y % 100)) / 100

w = (d + floor(2.6 * m - 0.2) + y + floor(y/4) + floor(c/4) - 2*c) % 7

return int(w)

"""
Compute the number of months starting on a given day of the week in a century
"""
def months_start_range(day,year_start,year_end):
    total = 0
    for year in range(year_start, year_end + 1):
        for month in range(1,13):
            if day_of_week(year, month, 1) == day: total += 1
    return total

start = time.time()

total = months_start_range(0,1901,2000)

elapsed = time.time() - start

print("%s found in %s seconds") % (total,elapsed)

This might you solve the problem.

It took around 0.068 seconds to solve it.

Upvotes: 1

Sait
Sait

Reputation: 19825

The mistakes you have:

  1. The way you calculate leap years
  2. Dictionary does not keep the order necessarily
  3. You assume January 1st is Sunday

The correct program would be:

from collections import OrderedDict

months = OrderedDict( [("January",31),("February", 28),("March",31),
                       ("April", 30), ("May", 31), ("June", 30),
                       ("July", 31), ("August", 31), ("September", 30),
                       ("October", 31), ("November", 30), ("December", 31)] )

days = ['Tuesday','Wednesday', 'Thursday','Friday','Saturday', 'Sunday', 'Monday']

day = 0
sunday_count = 0

def isLeap(year): #https://en.wikipedia.org/wiki/Leap_year#Algorithm
  leap = True
  if year % 4 != 0:
     leap = False
  elif year % 100 != 0:
     leap = True
  elif year % 400 != 0:
     leap = False
  return leap

for year in xrange(1901,2001):
  leap = isLeap(year)

  for m in months:
      dayName = days[day%7]
      if dayName == "Sunday":
         sunday_count += 1
      #print year, m, dayName
      day += months[m]
      if leap == True and m == "February":
          day += 1

print sunday_count
# print 171

Also, some days:

1901 January Tuesday
1901 February Friday
1901 March Friday
1901 April Monday
1901 May Wednesday
1901 June Saturday
1901 July Monday
1901 August Thursday
1901 September Sunday
...

Upvotes: 0

interjay
interjay

Reputation: 110174

You're iterating over the months dict, expecting it to iterate in the order of the months, but dicts aren't ordered, so you can get the months in the wrong order.

Since you don't actually need the month names, you can just make months a list of the month lengths instead.

Upvotes: 7

Klaus D.
Klaus D.

Reputation: 14379

You should use the datetime library, which will handled all the leap year information automatically:

from datetime import date
from collections import Counter

counter = Counter()

for year in xrange(1901, 2001):
    for month in xrange(1, 13):
        day = date(year, month, 1)
        counter[day.weekday()] += 1

print counter[6]

Upvotes: 2

Related Questions