{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Solving problems"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Your age in days"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Given your birthdate and the current date calculate your age in days. Compensate for leap years. Do not use any specific function not yet mentioned in the course... no nerdish trick."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In case you have not yet understood, this exercise is just an excuse to teach you how to tackle problems like this. We do not need a code to do this... Excel can do it with a single click!\n",
"
Searching for a solution with Google is fine if it weren't a practise problem... but not now!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Whenever you approach a problem, he biggest mistake you can make is rushing to writing the full code without having the solution clearly in mind."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Understanding the problem"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 0. Do not panic!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 1. What are the inputs ?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"two dates, current date and birthdate, and the calendar rules (not a real input)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 1a. how can they be written in a non-ambigous form?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"3 variables per date: year, month, day"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 1b. Which problems can they have?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- wrong type (string or float or bool instead of int)\n",
"- month not between 1 and 12, day not between 1 and 31, day 29 30 31 when the month does not have enough days\n",
"- birthdate after today\n",
"- year before 1582"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Decide whether tackling all the problems in the first version of your program or postpone some of them for later development."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"let's postpone"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2. What are the outputs ?"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"integer number, positive"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3. Solve the problem!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- start writing the code\n",
"- do some examples on paper"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 3a. Examples"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- 7/12/2012 6/12/2012: rejected\n",
"- 7/12/2012 8/12/2012: compare birthyear with currentyear and realise they are equal, compare birthmonth with currentmonth and realise they are equal, result is just currentday - birthday\n",
"- 8/12/2012 7/12/2012: rejected\n",
"- 23/6/2012 29/6/2013: compare the years and realise that they are different. Oh s*t, you have to go through all the calculations (deal later) --> 368\n",
"- 12/8/2012 31/6/2013: same as above, with extra difficulty that we cannot use human tricks here."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 3b. How would you, human, solve it?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you try to solve a problem that you cannot solve manually... you are in troubles! It can be done only if you have a huge theoretical background!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Try to do it for 26/1/2019 29/6/2019. How would a human do it?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- same year, lucky!!!\n",
"- month is different. So we consider the months IN BETWEEN (February, March, April, May). These have 28+31+30+31 days. Then we consider January which has 27,28,29,30,31 (so 31-26=5) 5 days. Then we consider June, which has from 1 to 29 INCLUDED. So 29-1+1=29 days. Final result is 120+5+29= 154 days\n",
"\n",
"In general, to solve it split the problem into: \n",
"- same month and same year (very easy), \n",
"- same year and contigous month (easy), \n",
"- same year and non-contigous month (as above), \n",
"- different year (good luck!)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 3c. Do we have a better way, i.e. more mechanical and thus easier to write?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Probably it is the approach a child would take.\n",
"
We simply start from the birthdate and count the days advancing on and on until we reach today. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## First attempt"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def differenceDays(by,bm,bd,ty,tm,td):\n",
" result=0\n",
" # we now start advanding from bd/bm/by until we reach td/tm/ty\n",
" # every time incrementing result by 1\n",
" while isDateBefore(by,bm,bd,ty,tm,td):\n",
" result=result+1\n",
" by,bm,bd=advanceByOneDay(by,bm,bd)\n",
" return result"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We clearly need...\n",
"- **isDateBefore** which tells us whether bd/bm/by is before td/tm/ty\n",
"- **advanceByOneDay** which increments bd most of the times, however must check whether it is the last day of the month and in this case... a mess..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Again, how would you, human, solve it?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Compare first the year, then the month, then the day"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def isDateBefore(by,bm,bd,ty,tm,td): # 28/12/2014 15/11/2016\n",
" if byty:\n",
" return False\n",
" elif bmtm:\n",
" return False\n",
" elif bdtd:\n",
" return False\n",
" else:\n",
" return False # two dates are the same!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Do not forget to test it!!!"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"False\n",
"True\n",
"False\n",
"True\n",
"True\n"
]
}
],
"source": [
"print(isDateBefore(2019,3,6,2019,3,6))\n",
"print(isDateBefore(2019,3,6,2019,3,7))\n",
"print(isDateBefore(2019,3,6,2019,3,4))\n",
"print(isDateBefore(2017,3,6,2019,3,4))\n",
"print(isDateBefore(2019,2,1,2019,3,4))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We now need to create advanceByOneDay(y,m,d)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- if the day is not the last one of the month, easy peasy increment d by 1\n",
"- if the day is the last one of the month, if the month is not December, then increment the month by 1 and reset the day to 1\n",
"- if the day is the last one of the month and month is December, result is 1 January of the next year"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def isLeapYear(y):\n",
" if y%4!=0:\n",
" return False\n",
" elif y%100!=0 or y%400==0:\n",
" return True\n",
" else:\n",
" return False\n",
" \n",
"def numberOfDays(m,y): \n",
" if m==2:\n",
" if isLeapYear(y):\n",
" return 29\n",
" else:\n",
" return 28\n",
" elif m==11 or m==4 or m==6 or m==9:\n",
" return 30\n",
" else:\n",
" return 31\n",
"\n",
"def advanceByOneDay(y,m,d):\n",
" if d 3N int divisions + 8N int comparisons\n",
"\n",
"def isDateBefore(by,bm,bd,ty,tm,td): # 28/11/2016 15/11/2016\n",
" if byty:\n",
" return False\n",
" elif bmtm:\n",
" return False\n",
" elif bdtd:\n",
" return False\n",
" else:\n",
" return False # two dates are the same!\n",
"\n",
"def isLeapYear(y):\n",
" if y%4!=0:\n",
" return False\n",
" elif y%100!=0 or y%400==0:\n",
" return True\n",
" else:\n",
" return False\n",
" \n",
"def numberOfDays(m,y): \n",
" if m==2:\n",
" if isLeapYear(y):\n",
" return 29\n",
" else:\n",
" return 28\n",
" elif m==11 or m==4 or m==6 or m==9:\n",
" return 30\n",
" else:\n",
" return 31\n",
"\n",
"def advanceByOneDay(y,m,d):\n",
" if d12 or tm<1 or tm>12 or bd<1 or td<1 or bd>numberOfDays(bm,by) or td>numberOfDays(tm,ty):\n",
" print(\"hey! Wrong input numbers\",by,bm,bd,ty,tm,td) \n",
"# year before 1582\n",
" elif by<1582 or ty<1582:\n",
" print(\"hey! Non-Gregorian calendar\",by,ty) \n",
"# birthdate after today\n",
" elif isDateBefore(by,bm,bd,ty,tm,td)==False:\n",
" print(\"Birth date must be before today\",by,bm,bd,ty,tm,td)\n",
" else:\n",
" result=0\n",
" # we now start advanding from bd/bm/by until we reach td/tm/ty\n",
" # every time incrementing result by 1\n",
" while isDateBefore(by,bm,bd,ty,tm,td):\n",
" result=result+1\n",
" by,bm,bd=advanceByOneDay(by,bm,bd)\n",
" return result"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Birth date must be before today 2022 6 10 2021 3 12\n",
"None\n",
"hey! Wrong input numbers 1984 13 15 2021 3 12\n",
"None\n",
"hey! Wrong input numbers 1984 6 31 2021 3 12\n",
"None\n",
"hey! Wrong input 1984 6.5 31 2021 3 12\n",
"None\n"
]
}
],
"source": [
"print(differenceDays(2022,6,10,2021,3,12))\n",
"print(differenceDays(1984,13,15,2021,3,12))\n",
"print(differenceDays(1984,6,31,2021,3,12))\n",
"print(differenceDays(1984,6.5,31,2021,3,12))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Alternative approach"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We try to build now the calculation that a human with competencies in calendar would do!\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def isLeapYear(y):\n",
" if y%4!=0:\n",
" return False\n",
" elif y%100!=0 or y%400==0:\n",
" return True\n",
" else:\n",
" return False\n",
" \n",
"def numberOfDays(m,y): \n",
" if m==2:\n",
" if isLeapYear(y):\n",
" return 29\n",
" else:\n",
" return 28\n",
" elif m==11 or m==4 or m==6 or m==9:\n",
" return 30\n",
" else:\n",
" return 31\n",
"\n",
"\n",
"def differenceDays(by,bm,bd,ty,tm,td):\n",
" # write your code here\n",
"\n",
"print differenceDays(2019,1,24,2019,6,29)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Which problems does it have?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- we are not considering dates in two different YEARS!\n",
"\n",
"It is easy but long to do it, it needs a while loop (and you will do it at home)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def differenceDays(by,bm,bd,ty,tm,td):\n",
" # write your code here\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Complexity issues"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The first program is much much easier to write, but is it more efficient? Imagine a case in which you have to calculate differences for very long periods, such as centuries, and calculate the complexity of the two algorithms."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
}
},
"nbformat": 4,
"nbformat_minor": 2
}