How to count cumulative calls of the Fibonacci function? - python

I tried :
def fibonnaci(n):
total_call = 0
if n ==0 or n == 1:
return 1
else:
if n== 2 or n == 1:
total_call +=0
else:
total_call +=2
return fibonnaci(n - 1) + fibonnaci(n - 2), total_call
n = 8
print(fibonnaci(n))
but I got a error:
TypeError: can only concatenate tuple (not "int") to tuple
How to display the number of calls for fibonnaci?

In your return statement the result of both fibonnaci(n - 1) and fibonnaci(n - 2) could be a tuple (argument > 1), or a single integer (argument <= 1) thus + means concatenation when the first one is a tuple. But when n == 3 in return fibonacci(n - 1) + fibonacci(n - 2), total_call fibonacci(2) is a tuple ((2, total_call)), while fibonacci(1) is an integer (1). So you want to concatenate a tuple with with an integer, which is impossible.

Using Decorators
Using Function Attributes
Reference
Code
def call_counter(func):
" Does the call count for any function "
def helper(x):
helper.calls += 1
return func(x)
helper.calls = 0
return helper
#call_counter
def fib(n):
if n ==0 or n == 1:
return 1
return fib(n - 1) + fib(n - 2)
Usage
fib(5)
print(fib.calls)
fib(10)
print(fib.calls) # Keeps running total so will be from previous
# fib(5) plus current fib(10)
# To reset counter
fib.calls = 0
Using Class
Reference
Code
class countCalls(object):
"""Decorator that keeps track of the number of times a function is called.
::
>>> #countCalls
... def foo():
... return "spam"
...
>>> for _ in range(10)
... foo()
...
>>> foo.count()
10
>>> countCalls.counts()
{'foo': 10}
Found in the Pythod Decorator Library from http://wiki.python.org/moin web site.
"""
instances = {}
def __init__(self, func):
self.func = func
self.numcalls = 0
countCalls.instances[func] = self
def __call__(self, *args, **kwargs):
self.numcalls += 1
return self.func(*args, **kwargs)
def count(self):
"Return the number of times this function was called."
return countCalls.instances[self.func].numcalls
#staticmethod
def counts():
"Return a dict of {function: # of calls} for all registered functions."
return dict([(func.__name__, countCalls.instances[func].numcalls) for func in countCalls.instances])
#countCalls
def fib(n):
if n ==0 or n == 1:
return 1
return fib(n - 1) + fib(n - 2)
Example
print(fib(3)) # Output 3
print(fib.count()) # Output 5
Advantage
Allows obtaining counts of all registered functions (i.e. registered by using decorator)
#countCalls
def f(n):
pass # dummy function
#countCalls
def g(n):
pass # dummy function
for i in range(5):
f(i)
for i in range(10):
g(i)
print(countCalls.counts())
# Outputs: {'f': 5, 'g': 10}

def fib(n):
if n <= 1:
return n, 1
fib_one = fib(n - 1)
fib_two = fib(n - 2)
#Return the result and the number of function calls (+ 1 for the current call)
return fib_one[0] + fib_two[0], fib_one[1] + fib_two[1] + 1
if __name__ == '__main__':
number_of_function_calls = fib(4)[1]
Fib(4) should return 9, which it does
fib(4)
fib(3) fib(2)
fib(2) fib(1) fib(1) fib(0)
fib(1) fib(0)

The problem is "obvious", if you bother to trace the values you're using:
return fibonnaci(n - 1) + fibonnaci(n - 2), total_call
When n is 3, this tries to "add" fibonnaci(2), a tuple, and fibonnaci(1), the integer 1. This is not a legal operation. You need to regularize your return values. You can't magically return the value alone (not the count) when that's what you want; you have to explicitly program the difference: dismember the tuple and add the component values.
Start with your base case being
return 1, 0
Your recursion case needs to add the components. Implementation is left s an exercise for the student.

Here's another answer using a decorator. The advantage of using a decorator is that the base function fib does not need to change. That means the total_count code and multiple return values can be discarded from your original attempt -
#counter(model = fib_result)
def fib(n = 0):
if n < 2:
return n
else:
return fib(n - 1) + fib(n - 2)
Our decorator counter accepts a model so we can respond to the behavior of the decorated function. Our decorated fib will return a fib_result such as { result: ?, count: ? }. That means we also need to handle fib(?) + fib(?) which is why we also defined __add__ -
class fib_result:
def __init__(self, result, count = 0):
if isinstance(result, fib_result):
self.result = result.result
self.count = result.count + count
else:
self.result = result
self.count = count
def __add__(a, b):
return fib_result(a.result + b.result, a.count + b.count)
As you can see, this fib_result is specific to counting fib calls. A different model may be required for counting other recursive functions that return other types of results.
All that's left now is to define our generic counter decorator. Our decorator takes arguments and returns a new decorator, lambda f: ..., which simply captures the passed arguments, lambda *args: ..., and constructs a new model with the result of f(*args) and a base count of 1 -
def counter(model = tuple):
return lambda f: lambda *args: model(f(*args), 1)
Here's how the complete program works -
r = fib(4)
print(f"answer: {r.result}, recurs: {r.count}")
# answer: 3, recurs: 9
r = fib(10)
print(f"answer: {r.result}, recurs: {r.count}")
# answer: 55, recurs: 177

Related

Creating Polynomial Class in Python

I am currently working on creating a Polynomial class that includes add , mul and eval methods. I'm currently stuck on the addition portion, if someone could give me some help on how to get that figured out that would be greatly appreciated. Everything currently works without errors but when I do p3 = p1 + p2 and I print p3 I get back the two lists together. Any feedback would be greatly appreciated.
class Polynomial(object):
def __init__(self, *coeffs, num = 0):
self.coeffs = list(coeffs) # turned into a list
assert (type(num) is type(1))
self.num = num
# Needs to be operator overload
'''
def __mul__(self, other):
'''
def __eval__(self, other, coeff, x):
result = coeff[-1]
for i in range(-2, -len(coeff)-1, -1):
result = result * x + coeff[i]
return result
def __add__(self, other):
assert type(other) is Polynomial
num = self.coeffs + other.coeffs
return Polynomial(num)
def __sub__(self, other):
assert type(other) is Polynomial
num = self.coeffs - other.coeffs
return Polynomial(num)
def __represntation__(self):
return "Polynomial" + str(self.coeffs)
def __str__(self):
rep = ""
degree = len(self.coeffs) - 1
rep += str(self.coeffs[0]) + "x^" + str(degree)
for i in range(1, len(self.coeffs)-1):
coeff = self.coeffs[i]
if coeff < 0:
rep += " - " + str(-coeff) + "x^" + str(degree - i)
else:
rep += " + " + str(coeff) + "x^" + str(degree - i)
if self.coeffs[-1] < 0:
rep += " - " + str(-self.coeffs[-1])
else:
rep += " + " + str(self.coeffs[-1])
return rep
You cannot directly add two lists.
def __add__(self, other):
assert type(other) is Polynomial
assert len(self.coeffs) != len(other.coeffs)
new_ceffs = [item1 + item2 for (item1, item2) in zip(self.coeffs, other.coeffs)]
return Polynomial(new_ceffs)
The problem is here:
num = self.coeffs + other.coeffs
Adding one list to another list concatenates them. To simply add corresponding elements to each other, you'd want to do
from itertools import zip_longest
...
num = [a + b for (a, b) in zip_longest(self.coeffs, other.coeffs, fillvalue=0)]
We use zip_longest() instead of the more generic zip() because one polynomial is possibly longer than the other and we don't want to ruin it. Either one of them will group together the corresponding elements so that we can easily add them and make a list of those.
You would do something similar for subtraction.
you should reverse the order of coefficients passed to your constructor so that indexes in the self.coeffs list correspond to the exponents. This would simplify the rest of your code and allow you to use zip_longest for additions and subtractions.
When you get to other operations however, I think you will realize that your internal structure would be easier to manage if it was a dictionary. A dictionary is more permissive of missing entries thus avoiding preoccupations of allocating spaces for new indexes.
class Polynomial(object):
def __init__(self, *coeffs):
self.coeffs = {exp:c for exp,c in enumerate(coeffs[::-1])}
def __add__(self, other):
assert type(other) is Polynomial
result = Polynomial(0)
result.coeffs = {**self.coeffs}
for exp,c in other.coeffs.items():
result.coeffs[exp] = result.coeffs.get(exp,0) + c
return result
def __sub__(self, other):
assert type(other) is Polynomial
result = Polynomial(0)
result.coeffs = {**self.coeffs}
for exp,c in other.coeffs.items():
result.coeffs[exp] = result.coeffs.get(exp,0) - c
return result
def __mul__(self, other):
assert type(other) is Polynomial
result = Polynomial(0)
for exp1,c1 in self.coeffs.items():
for exp2,c2 in other.coeffs.items():
result.coeffs[exp1+exp2] = result.coeffs.get(exp1+exp2,0) + c1*c2
return result
def __representation__(self):
return "Polynomial" + str(self.coeffs)
def __str__(self):
result = [""]+[f"{c}x^{i}" for i,c in sorted(self.coeffs.items()) if c]+[""]
result = "+".join(reversed(result))
result = result.replace("+1x","+x")
result = result.replace("-1x","-x")
result = result.replace("x^0","")
result = result.replace("x^1+","x+")
result = result.replace("+-","-")
result = result.strip("+")
result = result.replace("+"," + ")
result = result[:1]+result[1:].replace("-"," - ")
return result.strip()

How to call a python class like calling a function

Searching to build up my personal style of programming i want to be able to call a python class the same way i'd call a python function.
Here's what i mean:
consider this function:
def Factorial(n):
if n == 0:
return 1
else:
return n * Factorial(n - 1)
This is a function that outputs 24 when you call Factorial(4).
Now let's consider a class instead:
class Factorial:
def __call__(n):
if n == 0:
return 1
else:
return n * Factorial()(n - 1)
This code works the same way as the previous code except at call time where you instead write:
Factorial()(4) # which outputs 24
Now my question is how could you do this instead:
Factorial(4) # just that, and output 24, from THE object.
Thanks!
Use self.
class Factorial:
def __call__(self, n):
if n == 0:
return 1
else:
return n * self(n - 1)
f = Factorial()
f(3)
# 6
f(5)
# 120
From there you can easily add a starting value in your __new__ constructor.
class Factorial:
def __new__(cls, value=None):
instance = super().__new__(cls)
if value is None:
return instance
else:
return instance(value)
def __call__(self, n):
if n == 0:
return 1
else:
return n * self(n - 1)
Now you can
Factorial(5)
# 120
f = Factorial()
f(5)
# 120
Use a constructor (which is __init__ in Python) like so:
class Factorial:
def __init__(self, n):
// code here
You can override __new__ method:
class Factorial:
def __new__(cls, n):
if n == 0:
return 1
else:
return n * Factorial(n - 1)
Although the above solution works, I prefer a decorator-based solution which is less magical and is more intuitive and explicit:
def function_factory(cls):
return cls()
#function_factory
class Factorial:
def __call__(self, n):
if n == 0:
return 1
else:
return n * Factorial(n - 1)
A short & simple change in your existing code:
>>> class Factorial:
... def __call__(self,n):
... if n == 0:
... return 1
... else:
... return n * self.__call__(n - 1)
...
>>> Factorial = Factorial()
>>> Factorial(4)
24
>>>

How to access decorator attributes?

Is it possible to access decorator attributes in Python 3?
For example: is it possible to access self.misses after the call to the decorated fibonacci method?
class Cache:
def __init__(self, func):
self.func = func
self.cache = {}
self.misses = 0
def __call__(self, *args):
if not (args in self.cache):
self.misses += 1
self.cache[args] = self.func(*args)
return self.cache[args]
#Cache
def fibonacci(n):
return n if n in (0, 1) else fibonacci(n - 1) + fibonacci(n - 2)
fibonacci(20)
### now we want to print the number of cache misses ###
When you decorate a function (or class, or anything else), you're actually replacing the decorated object with the return value of the decorator. That means that this:
#Cache
def fibonacci(n):
return n if n in (0, 1) else fibonacci(n - 1) + fibonacci(n - 2)
is equivalent to this:
def fibonacci(n):
return n if n in (0, 1) else fibonacci(n - 1) + fibonacci(n - 2)
fibonacci = Cache(fibonacci)
Consequently, fibonacci is now a Cache instance and not a function:
>>> fibonacci
<__main__.Cache object at 0x7fd4d8b63e80>
So in order to get the number of cache misses, you just need to access fibonacci's misses attribute:
>>> fibonacci.misses
21

Looping through *args elements without a loop

I am trying to print out a succession of coefficients of a simple polynomial using *args
I roughly understand how *args works and I know how to use a simple loop to print out each coefficient, but in a __repr__ function that is supposed to return only one thing as the return value of function, I am confused how to do this...
class Polynomial:
def __init__(self, *coefficients):
self.coeffs = coefficients
def __repr__(self):
return "Polynomial( {} )".format(self.coeffs)
p1 = Polynomial(1, 2, 3)
p2 = Polynomial(3, 4, 3)
print(p1) # Polynomial( (1, 2, 3) )
print(p2) # Polynomial( (3, 4, 3) )
The outcome of print is what comes after the comments obviously, though what I am after is this format:
1x^2 + 2x + 3
3x^2 + 4x + 3
I have tried the following, but I cannot seem to get it right.
def __repr__(self):
# return "Polynomial( {}x^2 + {}x + {} )".format(self.coeffs)
# return "Polynomial( {0}x^2 + {1}x + {2} )".format(self.coeffs)
# return "Polynomial( {0}x^2 + {1}x + {2} )".format( enumerate(self.coeffs) )
Is there a neat way of doing this without having to loop through the args elements and all in one go within the return statement?
You can use the * syntax in __repr__ as well:
def __repr__(self):
return "Polynomial( {}x^2 + {}x + {} )".format(*self.coeffs)
However, if you want to have __repr__ adjust to the degree of the polynomial, you probably need a loop of some sort:
def __repr__(self):
degree = len(self.coeffs) - 1
polystr = ' + '.join('{}x^{}'.format(term, degree-i)
for i, term in enumerate(self.coeffs))
return "Polynomial( {} )".format(polystr)
Try this (I'm taking the coefficients as a list):
exp = len(self.coeffs)
i = 0
s = ""
while exp > -1:
s += str(coeffs[i]) + "x^" + str(exp) + " "
exp -= -1
i += 1
return s
Or, if you'll always have 2 as the highest exponent, you could use * to select all of the coeffs, as in .format(*self.coeffs).

Difference between Python decorator with and without syntactic sugar?

I'm trying to implement a decorator that memoizes an arbitrary function. It appears I've successfully accomplished that with the following code:
def memoize(func):
cache = {}
def wrapper(*args, **kwargs):
acc = ""
for arg in args:
acc += str(arg)
if acc in cache:
return cache[acc]
else:
cache[acc] = func(*args, **kwargs)
return cache[acc]
return wrapper
#memoize
def fib(n):
if n == 0 or n == 1:
return 1
else:
return fib(n - 1) + fib(n - 2)
Then fib(100) returns 573147844013817084101 fairly quickly. However, if I don't use the syntactic sugar:
def fib(n):
if n == 0 or n == 1:
return 1
else:
return fib(n - 1) + fib(n - 2)
memoized = memoize(fib)
print memoized(100)
The function hangs. Debugging it, it looks like the wrapper returned is unable to modify the cache. Can someone explain this behavior? As far as I know there shouldn't be a difference between using the sugar and not using the sugar.
Your recursive call is not memoized, because you used a new name, not the original fib function name. Each fib() iteration calls back to fib(), but that will call the original, undecorated function.
Assign the return value of the decorator call to fib instead:
def fib(n):
if n == 0 or n == 1:
return 1
else:
return fib(n - 1) + fib(n - 2)
fib = memoize(fib)
print fib(100)
Alternatively, if you must use memoize as the name of the decorator result, have fib() call memoized() for recursive calls:
def fib(n):
if n == 0 or n == 1:
return 1
else:
return memoized(n - 1) + memoizzed(n - 2)
memoized = memoized(fib)
print memoized(100)
Remember, the #decorator syntax assigns to the same name, not a new name. The following two are equivalent:
#memoize
def fib(n):
# ....
and
def fib(n):
# ....
fib = memoize(fib) # Same name!
except the name fib is never bound to the original function first.

Categories

Resources