#!/usr/bin/env python
# coding: utf-8
#
cs1001.py , Tel Aviv University, Fall 2017-2018
#
# # Recitation 4
# We talked about comlexity. First we had an intuitive intro through an exercise and then we formally defined the notion of $O$ and solved some theoretical exercises.
#
# #### Takeaways:
#
#
# - The time complexity bound implies how that the running time changes as a function of the input size.
# - Two solutions that have the same time complexity bound may differ in their running time (one can be more efficient than the other).
# - The Big O notation "hides" additive and multiplicative constants
# - Two solutions that have the same time complexity bound may differ in their running time (one can be more efficient than the other).
# - To analyze nested loops that are dependent, use a sum ($\sum$), with the boundaries of the external loop as the limits and the number of iterations for the inner loop in the sum itself.
#
# ## Exercise: Given an natural number $p$, how many right triangles exist with integer sized sides whose perimeter is $p$?
# ### Model:
#
# How many triplets (a,b,c) are there such that:
#
#
# - $a^2 + b^2 = c^2$
# - $a + b + c = p$
# - $a,b,c > 0$ are all integers
#
#
# ### In order to avoid counting a triplet twice or more, we require:
#
#
# - $0 < a < b < c$
#
#
# ### Note:
#
#
# - $a \neq b$ and $b \neq c$
# - The condition $b < c$ is redundant (since we require that $a,b,c > 0$ and $a^2 + b^2 = c^2$)
#
# When computing code complexity, we
#
# - define the entire content of the innermost loop as an “atomic operation” which takes constant time.
# - describe the complexity as a function of $p$ (i.e., use $p$ as the “problem size” or “input size”).
# Note, however, that the formal definition of “problem size” for a numeric input is the size it takes to represent the input, i.e. $n = \log(p)$.
# Working with $p$ here is easier and more intuitive.
#
# # Trivial solution (v1):
# In[3]:
def number_of_integer_right_triangles_v1(p):
cnt = 0
for a in range(1,p):
for b in range(1,p):
for c in range(1,p):
if a + b + c == p and a < b and a*a + b*b == c*c:
cnt += 1
return cnt
# ### Analysis:
#
# $(p-1)^3$ iterations.
#
# The complexity is $O(p^3)$ (cubic complexity)
# # Second version (v2):
# Once we set $a,b$ and $p$, $c$ is already defined!
# In[4]:
def number_of_integer_right_triangles_v2(p):
cnt = 0
for a in range(1,p):
for b in range(1,p):
c = p - a - b
if a < b and a*a + b*b == c*c:
cnt += 1
return cnt
# ### Analysis:
#
# $(p-1)^2$ iterations.
#
# The complexity is $O(p^2)$ (squared complexity)
# # Third version (v3):
# We require $an_0$
# $|f(n)| \leq c\cdot|g(n)|$
# ### Time complexity hierarchy
#
# Let $n$ denote the size of the input.
# The most common time complexities we encounter are :
#
# * Constant - $O(1)$
# * Logarithmic - $O(\log(n))$
# * Root/fractional - $O(n^{1/c})$ for $c>1$
# * Linear - $O(n)$
# * Loglinear - $O(n \log(n))$
# * Polynomial - $O(n^{c})$ for $c>1$
# * Exponential - $O(c^{n})$ for $c>1$
#
# See also this list on [Wikipedia](http://en.wikipedia.org/wiki/Time_complexity#Table_of_common_time_complexities).
# ## Prove or Disprove examples
# See some examples [here](http://tau-cs1001-py.wdfiles.com/local--files/recitation-logs-2017a/complexity_prove_or_disprove.pdf)