Creating Polynomial Class in Python - 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()

Related

Clean way to create 'n-digit precision' float child class in Python

I want to create a LimitedPrecisionFloat class that would allow me to store a float with n-digits (I don't want to round the number, just cut the remaining digits). This is how I would like it to behave:
a = LimitedPrecisionFloat(3.45678, ndigits=3)
print(a) # 3.456
a += 0.11199
print(a) # 3.567
a -= 0.567999
print(a) # 3 (or 3.0 or 3.000)
print(a + 1.9999) # 4.999
# same with other arithmetic operators, ie.
print(LimitedPrecisionFloat(1.11111 * 9.0, 3) # 9.999
...
What I currently have is:
def round_down(x:float, ndigits:int):
str_x = str(x)
int_part = str(int(x))
fraction_part = str_x[str_x.find('.') + 1:]
fraction_part = fraction_part[:ndigits]
return float(int_part + '.' + fraction_part)
class LimitedPrecisionFloat(float):
def __new__(cls, x, ndigits:int):
return super(LimitedPrecisionFloat, cls).__new__(cls, round_down(x, ndigits))
def __init__(self, x, ndigits: int):
super().__init__()
self.ndigits = ndigits
def __add__(self, other):
return round_down(float(self) + other, ndigits=self.ndigits)
def __sub__(self, other):
return round_down(float(self) - other, ndigits=self.ndigits)
# other operators
However, this will not work, because whenever __add__ (or any other operator) is called, it returns float and the precision is back in the number. So when I tried doing something like this:
def __add__(self, other):
return LimitedPrecisionFloat(float(self) + other, ndigits=self.ndigits)
But then I got the max recursion depth error.
Because of that, I also don't have a clue on how to implement augmented arithmetic assignments, like __iadd__.

I could not understand how does the __add__ function work here?

When framing if condition for 'add_length' , isinstance has 2nd parameter 'length' but in the case of 'add_inches' the 2nd parameter is given int(integer) . I could not understand this part .why can't they both be int(integer). Help is appreciated !
class Length:
def __init__(self, feet, inches):
self.feet = feet
self.inches = inches
def __str__(self):
return f'{self.feet} {self.inches}'
def __add__(self, other):
if isinstance(other, Length):
return self.add_length(other)
if isinstance(other, int):
return self.add_inches(other)
else:
return NotImplemented
def __radd__(self, other):
return self.__add__(other)
def add_length(self, L):
f = self.feet + L.feet
i = self.inches + L.inches
if i >= 12:
i = i - 12
f += 1
return Length(f, i)
def add_inches(self, inches):
f = self.feet + inches // 12
i = self.inches + inches % 12
if i >= 12:
i = i - 12
f += 1
return Length(f, i)
length1 = Length(2, 10)
length2 = Length(3, 5)
print(length1 + length2)
print(length1 + 2`
print(length1 + 20)
print(20 + length1)
The first condition is checking for the scenario when two length objects are added together:
length1 = Length(2, 10)
length2 = Length(3, 5)
print(length1 + length2)
The second condition is checking for the scenario when an integer is added to a length.
print(length1 + 2)
print(length1 + 20)
The conditional calls two different methods add_length and add_inches because of how the data needs to be accessed. That is, for add_length it accessed via properties of the object (L). For add_inches it is accessed from the integer (inches).
From my understanding, this is simply the way this class is designed.
You can either add two objects from the length class together (this is the first isinstance) or you can add an integer in inches to the length (this is the second isinstance).

a python class for modular integers that supports addition and negation?

I'm trying to come up with code for the definition addition and negation in python. The most similar thing I have is for multiplication, which I've copied below. Any ideas on what should be changed to account for addition and negation of modular integers? I know subtraction is just the addition of negation in this problem. Any help would be much appreciated!!
class Zp:
p = 2 #class attribute
def __init__(self, n):
self.n = n % Zp.p #instance attribute
def __str__(self):
#msg = '[' + str(self.n) + ']'
msg = str(self.n) + ' mod ' + str(Zp.p) + ')'
return msg
def __repr__(self):
return self.__str__()
# def __add__(self, other):
# [a], [b] in Zp ([a], [b] are sets of integers)
# [a]+[b] = [a+b] e.g. a+b mod p
# def __neg__(self, other):
# def __sub__ (self, other): # the addition of negation
def __mul__(self, other): # [a]*[b] = [a*b] e.g. a*b mod p
if Zp.__instancecheck__(other):
return Zp(self.n * other.n)
elif type(other) == int:
return Zp(self.n * other)
else:
raise TypeError('multiplication a Zp with a non-Zp or non-integer')
what I have tried for addition:
def __add__(self, other): # [a]*[b] = [a*b] e.g. a*b mod p
if Zp.__instancecheck__(other):
return Zp(self.n + other.n)
elif type(other) == int:
return Zp(self.n + other)
When doing modular arithmetic on numbers, you can generally do the normal operation (adding, subtracting, multiplying), then take the answer mod your modulus.
For example,
3 * 3 mod 2 = 9 mod 2 = 1 mod 2
In order to implement the operations you're specifying, do the operation, then take the modulo. Take a look at Wikipedia's article for more information about how to do modular arithmetic: Modular Arithmetic - Wikipedia

How can i implement Vector addition for the form [1,2,3,4] + v where v is vector and [1,2,3,4] is a list in python?

Here is the code for the program:
I tried to implement a vector class as i learned about operator overloading in python. I was able to make a vector class which can be used much like a list with operations like len(vector) , vector1 + vector2 (addition operator overloading) and subtraction . But i found a problem. Here is the code of the program and i have stated the problem below :
class vector:
"""Initialize Vector"""
def __init__(self,d):
self.coords = [0]*d
def __len__(self):
return len(self.coords)
def __getitem__(self, item): #Getting an item from a vector
return self.coords[item]
def __setitem__(self, key, value):
self.coords[key] = value
def __add__(self, other):
if(len(self)!= len(other)):
print("Don't add these too ! they are not same types :P")
else:
result = vector(len(self))
for i in range(0,len(result)):
result[i] = self[i] + other[i]
return result
def __sub__(self, other):
if(len(self) != len(other)):
print("Dont subtract these two!")
else:
result = vector(len(self))
for i in range(0,len(result)):
result[i] = self[i] - other[i]
return result
def __eq__(self, other):
return self.coords == other.coords
def __ne__(self, other):
return self.coords != other.coords
def __str__(self):
return '<'+ str(self.coords)[1:-1] +'>'
print("Input for vector 1")
x = vector(2)
for i in range(0,len(x)):
x[i] = int(input('Enter a number\n'))
print("Input for vector 2")
y = vector(2)
for i in range(0,len(y)):
y[i] = int(input('Enter a number\n'))
z = x-y
print(str(x))
print(" + ")
print(str(y))
print(" = ")
print(str(z))
It works if i add a vector + list but list + vector gives an error. How can i implement the other .
You want to implement __radd__. Since it should do the same thing as __add__ here, you can just assign __add__ to it:
class vector:
...
def __add__(self, other):
if(len(self)!= len(other)):
print("Don't add these too ! they are not same types :P")
else:
result = vector(len(self))
for i in range(0,len(result)):
result[i] = self[i] + other[i]
return result
__radd__ = __add__
...

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

Categories

Resources