-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpolynomial.py
201 lines (163 loc) · 6.8 KB
/
polynomial.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# Copyright (c) 2010 Andrew Brown <[email protected], [email protected]>
# See LICENSE.txt for license terms
from StringIO import StringIO
from ffp import PF59int
class Polynomial(object):
"""Completely general polynomial class.
Polynomial objects are immutable.
Implementation note: while this class is mostly agnostic to the type of
coefficients used (as long as they support the usual mathematical
operations), the Polynomial class still assumes the additive identity and
multiplicative identity are 0 and 1 respectively. If you're doing math over
some strange field or using non-numbers as coefficients, this class will
need to be modified."""
def __init__(self, coefficients=(), **sparse):
"""
There are three ways to initialize a Polynomial object.
1) With a list, tuple, or other iterable, creates a polynomial using
the items as coefficients in order of decreasing power
2) With keyword arguments such as for example x3=5, sets the
coefficient of x^3 to be 5
3) With no arguments, creates an empty polynomial, equivalent to
Polynomial((0,))
>>> print Polynomial((5, 0, 0, 0, 0, 0))
5x^5
>>> print Polynomial(x32=5, x64=8)
8x^64 + 5x^32
>>> print Polynomial(x5=5, x9=4, x0=2)
4x^9 + 5x^5 + 2
"""
if coefficients and sparse:
raise TypeError("Specify coefficients list /or/ keyword terms, not"
" both")
if coefficients:
# Polynomial((1, 2, 3, ...))
c = list(coefficients)
# Expunge any leading 0 coefficients
while c and c[0] == 0:
c.pop(0)
if not c:
c.append(0)
self.coefficients = tuple(c)
elif sparse:
# Polynomial(x32=...)
powers = sparse.keys()
powers.sort(reverse=1)
# Not catching possible exceptions from the following line, let
# them bubble up.
highest = int(powers[0][1:])
coefficients = [0] * (highest+1)
for power, coeff in sparse.iteritems():
power = int(power[1:])
coefficients[highest - power] = coeff
self.coefficients = tuple(coefficients)
else:
# Polynomial()
self.coefficients = (0,)
def __len__(self):
"""Returns the number of terms in the polynomial"""
return len(self.coefficients)
def degree(self):
"""Returns the degree of the polynomial"""
return len(self.coefficients) - 1
def __add__(self, other):
diff = len(self) - len(other)
if diff > 0:
t1 = self.coefficients
t2 = (0,) * diff + other.coefficients
else:
t1 = (0,) * (-diff) + self.coefficients
t2 = other.coefficients
return self.__class__(x+y for x,y in zip(t1, t2))
def __neg__(self):
return self.__class__(-x for x in self.coefficients)
def __sub__(self, other):
return self + -other
def __mul__(self, other):
terms = [0] * (len(self) + len(other))
for i1, c1 in enumerate(reversed(self.coefficients)):
if c1 == 0:
# Optimization
continue
for i2, c2 in enumerate(reversed(other.coefficients)):
terms[i1+i2] += c1*c2
return self.__class__(reversed(terms))
def __floordiv__(self, other):
return divmod(self, other)[0]
def __mod__(self, other):
return divmod(self, other)[1]
def __divmod__(dividend, divisor):
"""Implements polynomial long-division recursively. I know this is
horribly inefficient, no need to rub it in. I know it can even throw
recursion depth errors on some versions of Python.
However, not being a math person myself, I implemented this from my
memory of how polynomial long division works. It's straightforward and
doesn't do anything fancy. There's no magic here.
"""
class_ = dividend.__class__
# See how many times the highest order term
# of the divisor can go into the highest order term of the dividend
dividend_power = dividend.degree()
dividend_coefficient = dividend.coefficients[0]
divisor_power = divisor.degree()
divisor_coefficient = divisor.coefficients[0]
quotient_power = dividend_power - divisor_power
if quotient_power < 0:
# Doesn't divide at all, return 0 for the quotient and the entire
# dividend as the remainder
return class_((PF59int(0),)), dividend
# Compute how many times the highest order term in the divisor goes
# into the dividend
quotient_coefficient = dividend_coefficient / divisor_coefficient
quotient = class_( (quotient_coefficient,) + (0,) * quotient_power )
remander = dividend - quotient * divisor
if remander.coefficients == (0,):
# Goes in evenly with no remainder, we're done
return quotient, remander
# There was a remainder, see how many times the remainder goes into the
# divisor
morequotient, remander = divmod(remander, divisor)
return quotient + morequotient, remander
def __eq__(self, other):
return self.coefficients == other.coefficients
def __ne__(self, other):
return self.coefficients != other.coefficients
def __hash__(self):
return hash(self.coefficients)
def __repr__(self):
n = self.__class__.__name__
return "%s(%r)" % (n, self.coefficients)
def __str__(self):
buf = StringIO()
l = len(self) - 1
for i, c in enumerate(self.coefficients):
if not c and i > 0:
continue
power = l - i
if c == 1 and power != 0:
c = ""
if power > 1:
buf.write("%sx^%s" % (c, power))
elif power == 1:
buf.write("%sx" % c)
else:
buf.write("%s" % c)
buf.write(" + ")
return buf.getvalue()[:-3]
def evaluate(self, x):
"Evaluate this polynomial at value x, returning the result."
# Holds the sum over each term in the polynomial
c = PF59int(0)
# Holds the current power of x. This is multiplied by x after each term
# in the polynomial is added up. Initialized to x^0 = 1
p = PF59int(1)
for term in reversed(self.coefficients):
c = c + term * p
p = p * x
return c
def get_coefficient(self, degree):
"""Returns the coefficient of the specified term"""
if degree > self.degree():
return 0
else:
return self.coefficients[-(degree+1)]