Project Euler #19, Python - 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 ?

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.

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]

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 > 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.

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);
}

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. ;-)

The mistakes you have:
The way you calculate leap years
Dictionary does not keep the order necessarily
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
...

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())

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))

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)

==>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)

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)

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

Note that the problem defines the first day of 1900 as Monday and you define the first day of 1901 as Monday.
months = [31,28,31,30,31,30,31,31,30,31,30,31]
def countingSundays():
day = 1
sunday_count = 0
for year in range(1900,1901):
for m in months:
day += m
if (year % 4 == 0 and m == 28):
day += 1
for year in range(1901,2001):
for m in months:
day += m
if (year % 4 == 0 and m == 28):
day += 1
if day % 7 == 0:
sunday_count += 1
return sunday_count
print ("Sundays:", countingSundays())

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

Related

Return number of weekdays for a specific date (Python)

I need to calculate a price based on a given date weekdays a month.
This is what im currently working with:
month = time.month
year = time.year
weekdays = 0
cal = calendar.Calendar()
for day in cal.itermonthdates(year, month):
if day.weekday() == 6 and day.month == month:
weekdays += 1
But this does not rely on a given date.
I want this to return 6 for the date 10.01.2020, or 6 for 03.01.2020 or 4 for 06.01.2020.
Any help would be very nice.
Following can be a dry approach:
import datetime
# ...
prev_day = time.day - datetime.timedelta(days=1)
month = time.month
year = time.year
cal = calendar.Calendar()
days_iterator = cal.itermonthdates(year, month)
while next(days_iterator) != prev_day:
pass
weekdays = 0
for d in days_iterator:
if d.weekday() == 6 and d.month == month:
weekdays += 1
Try this:
import datetime
d=datetime.date(2020, 1, 10) #Format YYYY, MM, DD
print(d.isoweekday())
Now this will print 5 not 6 as it is a Friday and the counting starts at Monday (using isoweekday instead of weekday will let the counting start by 1 instead of 0) but there should be an easy fix if you want your week begin on Sunday just add 1 and calculate modulo 7:
print((d.isoweekday()+1)%7)
https://docs.python.org/3/library/datetime.html

Get day from day number of the year?

How to write a program that asks the user the day number in year in the range 2 to 365 and asks the first day of year -
Sunday, Monday or Tuesday etc. Then program should display the day-number that has been input.
For example:
Input year = 2019
Input of day number = 144
First day of year = 'Tuesday'
Output = 'Friday' (-> 144th day of year 2019 = Friday)
Note:- you can not use "Date" and "Month" of that particular year
From this link of an algorithm, I tried to finding out day by using this as reference but could not find particular solution:
def day_of_week(year, month, day):
t = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4]
year -= month < 3
return (year + int(year/4) - int(year/100) + int(year/400) + t[month-1]
+ day) % 7
day = 28
month = 4
year = 2019
print(day_of_week(year, month, day))
Let's see if I understand you right...the program takes the year, the first day of the year (e.g. 'Tuesday'), and a number (e.g. 144), and it just outputs the day of the week corresponding to that number day in the year (e.g. 'Friday is the 144th day of 2019)?
If that's the problem, simply use mod by 7 to find the answer, like so:
def numberedDay(firstDay, dayNumber):
days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
firstIndex = days.index(firstDay)
endIndex = (firstIndex + int(dayNumber) - 1) % 7
return days[endIndex]
year = input("Year: ")
firstDay = input("First day of the year (e.g. 'Monday'): ")
dayNumber = input("Day number: ")
print(numberedDay(firstDay, dayNumber))
But as per condition of 'dayNumber' , it shoud be in the range between 2 to 365. I think this might be work...
# Solution - 2 With dayNumber in the Range betwwen 2 to 365...
import sys
def numberedDay(firstDay, dayNumber):
#firstDay = 'Wednesday'
#dayNumber = 319
# Made weekly days dictionary
days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday',
'Sunday']
# Automatically generate first index of days dictionary.. For example if firstDay
= 'Wednesday' then it will return firstDay=2
firstIndex = days.index(firstDay)
# Generating endIndex....
endIndex = (firstIndex + int(dayNumber) - 1) % 7
# endIndex = (2 + int(dayNumber) - 1) % 7
# endIndex = (2 + 319 - 1) % 7
# endIndex = (320) % 7
# endIndex = 5
return days[endIndex]
# return 5th element of days list which is = Saturday
# Input Year = 1997
year = input("Year: ")
# Input firstDay = Wednesday
firstDay = input("First day of the year (e.g. 'Monday'): ")
# Input dayNumber = 319
dayNumber = input("Day number: ")
#if int(dayNumber) > 365:
# raise sys.exit()
if int(dayNumber) in range(2,365):
# printing dayNumber given by user
dayNumber = int(dayNumber)
else:
# if dayNumber is greater than 365 then system will be exit.
raise sys.exit()
# printing output
print(numberedDay(firstDay, dayNumber))

how to get weekday from a given date in python?

2001-10-18
I want to calculate weekday e.g. Monday, Tuesday from the date given above. is it possible in python?
Here is one way to do this:
dt = '2001-10-18'
year, month, day = (int(x) for x in dt.split('-'))
answer = datetime.date(year, month, day).weekday()
There is the weekday() and isoweekday() methods for datetime objects.
Python doc
Here's what I have that got me if a leap year and also days in a given month (accounts for leap years). Finding out a specific day of the week, that I'm also stuck on.
def is_year_leap(year):
if (year & 4) == 0:
return True
if (year % 100) == 0:
return False
if (year % 400) == 0:
return True
return False
def days_in_month(year, month):
if month == 1 or month == 3 or month == 5 or month == 7 or month == 8 or month == 10 or month == 12:
return 31
if month == 2:
if is_year_leap(year):
return 29
else:
return 28
if month == 4 or month == 6 or month == 9 or month == 11:
return 31

Python only executes one if statement

I am extremely new at python and programming in general so pardon if the answer is obvious.
I am trying to write a program that will tell you what day Easter falls on through a range of years.
I am running into a problem with my if statements. If the year is one of the years specified in the first if statement, it will not execute the second if statement even if day > 31.
year = int(input('Input a year: '))
a = year % 19
b = year % 4
c = year % 7
d = (19 * a + 24) % 30
e = (2 * b + 4 * c + 6 * d + 5) % 7
day = 22 + d + e
month = 'March'
if year == 1954 or year == 1981 or year == 2049 or year == 2076:
day = day + 7
if day > 31:
day = day - 31
month = 'April'
print('Easter is on', month, day)
It is executing the second if statement.
Did you consider how large day would be?
If, for example, year == 2049, then:
a = 16
b = 1
c = 5
d = 28
e = 6
This leads to the initial assignment to day day = 22+d+e to set day = 56. In the first if, it is then set to 63
63-31 = 32, which is what you are getting? (along with 'april' as month, indicating that it was reaching the if statement the whole time)
So perhaps you've misunderstood the algorithm used for determining the Easter date and not implemented it correctly.
Issue is in your logic I believe. I modified the code a bit and found that the second IF is working
year = int(input('Input a year: '))
a = year % 19
b = year % 4
c = year % 7
d = (19 * a + 24) % 30
e = (2 * b + 4 * c + 6 * d + 5) % 7
day = 22 + d + e
month = 'March'
if year == 1954 or year == 1981 or year == 2049 or year == 2076:
day = day + 7
if day > 31:
print("day ", day)
day = day - 31
month = 'April'
print('Easter is on', month, day)
Gives the output :
Input a year: 1954
day 63
Easter is on April 32
If you see the result, print("day ", day) command is getting executed. Please revisit your logic. I think you should use the modulus instead of subtraction.

Why doesn't this project euler #19 code work? (Python)

I made a code for project euler's #19 problem in Python, and it doesn't give me the right answer.
The question is: You are given the following information, but you may prefer to do some research for yourself.
1 Jan 1900 was a Monday.
Thirty days has September,
April, June and November.
All the rest have thirty-one,
Saving February alone,
Which has twenty-eight, rain or shine.
And on leap years, twenty-nine.
A leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400.
How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)?
What is the problem here?:
months = {'January': 31,
'February': 28,
'March': 31,
'April': 30,
'May': 31,
'June': 30,
'July': 31,
'August': 31,
'September': 30,
'November': 30,
'December': 31
}
years = range(1900, 2001)
day = 1
def main():
global day
for year in years:
if year % 4 == 0 or year % 400 == 0:
months['February'] = 29
else:
months['February'] = 28
for month in months:
if months[month] == 31:
day += 31 % 7 +1
while day > 7:
day = day - 7
if day == 1:
yield day
result = sum(main())
print (result)
Also it generates different answers everytime I use,
Thanks :)
I don't understand the algorithm and your code. But I can say about this.
it generates different answers everytime I use
Probably you forgot to initialize month, year and day before executing result = sum(main()).
The solution will be simpler, if you use dateutil library:
In [16]: from datetime import datetime
In [17]: from dateutil.relativedelta import relativedelta
In [18]: current = datetime(1901, 1, 1)
In [19]: end = datetime(2001, 1, 1)
In [20]: ans = 0
In [21]: while current < end:
....: if current.weekday() == 6:
....: ans += 1
....: current += relativedelta(months=1)
....:
In [22]: ans
Out[22]: 171

Categories