C++ For Mathematicians (2006) [eng]
.pdf246 |
C++ for Mathematicians |
12.3.5 Equality
To check if two polynomials are equal, we make sure they have the same degree and that corresponding coefficients are equal. The operators == and != are given on lines 139–149.
12.3.6 Arithmetic
We provide methods for the usual arithmetic operators: addition, subtraction, multiplication, and division (quotient and remainder). See lines 153ff.
Each of the basic operators is defined like this:
Polynomial<K> operator+(const Polynomial<K>& that) const { ... }
If P and Q are of type Polynomial<double>, then the expression P+Q invokes this method with that referring to Q. However, the expression P+5 also engages this method; implicitly the 5 is cast to a polynomial via Polynomial<double>(5). However, the expression 5+P cannot be handled by this method (because 5 is not a polynomial). Therefore, we also provide procedure templates such as this:
template <class J, class K>
Polynomial<K> operator+(J x, const Polynomial<K>& P) { return P + K(x);
}
See lines 291–298.
In addition to the usual operators + - * / % we provide their operate/assign cousins: += -= *= /= %=. We also give methods for unary minus and exponentiation (to a nonnegative integer power).
With the exception of division, the code for these various operators is reasonably straightforward. Division requires a bit more attention.
As in the case of integers, division of polynomials produces two results: the quotient and the remainder. Let a(x) and b(x) be polynomials with b 6= 0. Then there exist polynomials q(x) and r(x) for which
a(x) = q(x)b(x) + r(x) |
and |
degr(x) < degb(x). |
||||
q and r are unique. For example, if a(x) = 3x2 +x |
− |
2 and b(x) = 2x+1, |
||||
Furthermore,3 |
1 |
7 |
|
|
|
|
then q(x) = 2 x − |
4 |
and r(x) = −4 . |
|
|
|
|
We define A/B and A%B to be the quotient and remainder, respectively, when we divide a(x) by b(x).
To this end, we define the procedure quot_rem(A,B,Q,R) (see lines 300–321) to find the quotient and remainder. The operator/ and operator% make use of quot_rem to do their work.
Please note that the division methods require that K be a field. If K is only a commutative ring (e.g., long integers), then most of the class template works fine, but the division methods do not.
Polynomials |
247 |
12.3.7 Output to the screen
The operator << for writing to the computer’s screen appears on lines 269–289. This operator writes the polynomial 5x3 −x + 12 like this:
(5)Xˆ3 + (-1)X + (0.5)
The procedure is smart enough to omit terms whose coefficient is zero, to omit the superscript 1 on the linear term, and to omit the variable altogether on the constant term. However, it does not convert (-1)X into the more legible - X, or even - (1)X. We might think that it is possible for the code to check if a coefficient is negative and modify behavior accordingly. However, for some fields K (such as C and Zp) this does not make sense.
12.3.8 GCD
Let a(x) and b(x) be polynomials. A common divisor of a(x) and b(x) is a polynomial c(x) with the property that there exist polynomials s(x) and t(x) so that a(x) = c(x)s(x) and b(x) = c(x)t(x). A greatest common divisor of a(x) and b(x) is a common divisor of highest possible degree.
Two polynomials may have several different greatest common divisors because any nonzero scalar multiple of a greatest common divisor is again a greatest common divisor.
To settle on a specific meaning for gcd[a(x),b(x)] we choose the greatest common divisor d(x) whose leading coefficient is 1. (A polynomial whose leading coefficient is 1 is called monic.) Any two nonzero polynomials have a unique monic greatest common divisor.
The gcd procedure for two polynomials is given on lines 323–336. This procedure uses the helper methods is_monic() and make_monic(); the former checks if the polynomial is monic and the latter transforms the polynomial into a monic polynomial by dividing by the leading coefficient. See lines 256–265.
We use the Euclidean algorithm (described in Section 3.3) to calculate the gcd of two polynomials.
In addition to the usual gcd procedure, we provide an extended version. Given polynomials a(x) and b(x), the extended gcd procedure finds d(x) = gcd[a(x),b(x)] as well as polynomials s(x) and t(x) so that d(x) = a(x)s(x) + b(x)t(x). See lines 337–376.
12.3.9 The code
Here is the listing of Polynomial.h. This includes the Polynomial<K> class template as well as associated procedure templates (operator<<, gcd, etc.).
Program 12.4: Header file for the Polynomial class template.
1 #ifndef POLYNOMIAL_H
2 #define POLYNOMIAL_H
248 |
C++ for Mathematicians |
3
4#include <vector>
5 #include <iostream>
6using namespace std;
7
8 template <class K>
9 class Polynomial {
10private:
11vector<K> coef;
12 |
long |
dg; |
13 |
|
|
14void deg_check() {
15dg = -1;
16for (long k=0; k< long(coef.size()); k++) {
17if (coef[k] != K(0)) dg = k;
18}
19coef.resize(dg+1);
20}
21
22public:
23Polynomial<K>() {
24clear();
25}
26
27template <class J>
28Polynomial<K>(J a) {
29coef.clear();
30coef.resize(1);
31coef[0] = K(a);
32deg_check();
33}
34
35template <class J, class JJ, class JJJ>
36Polynomial<K>(J a, JJ b, JJJ c) {
37coef.clear();
38coef.resize(3);
39coef[2] = K(a);
40coef[1] = K(b);
41coef[0] = K(c);
42deg_check();
43}
44
45template <class J>
46Polynomial<K>(long array_size, const J* array) {
47if (array_size < 0) {
48coef.clear();
49coef.resize(1);
50coef[0] = K(0);
51dg = -1;
52return;
53}
54coef.clear();
55coef.resize(array_size);
56for (long k=0; k<array_size; k++) {
57coef[k] = K(array[k]);
58}
Polynomials |
249 |
59deg_check();
60}
61
62 long deg() const { return dg; }
63
64K get(long k) const {
65if ( (k<0) || (k>dg) ) return K(0);
66return coef[k];
67}
68
69 K operator[](long k) const { return get(k); }
70
71void set(long idx, K a) {
72if (idx < 0) return;
73if (idx+1 >long(coef.size())) coef.resize(idx+1);
74coef[idx] = a;
75deg_check();
76}
77
78void clear() {
79coef.resize(1);
80dg = -1;
81coef[0] = K(0);
82}
83
84void minimize() {
85deg_check();
86coef.reserve(dg+1);
87}
88
89 bool isZero() const { return (dg < 0); }
90
91void shift(long n = 1) {
92if (n==0) return;
93
94if (n<0) {
95for (long k=0; k<=dg+n; k++) coef[k] = coef[k-n];
96for (long k=dg+n+1; k<=dg; k++) coef[k] = K(0);
97deg_check();
98return;
99}
100
101coef.resize(n+dg+1);
102for (long k=dg; k>=0; k--) coef[k+n] = coef[k];
103for (long k=0; k<n; k++) coef[k] = K(0);
104dg += n;
105}
106
107 // FUNCTION APPLICATION //
108
109K of(K a) const {
110if (dg <= 0) return coef[0];
111K ans;
112ans = K(0);
113for (long k=dg; k>=0; k--) {
114ans *= a;
250 |
C++ for Mathematicians |
115ans += coef[k];
116}
117return ans;
118}
119
120 K operator()(K a) const { return of(a); }
121
122Polynomial<K> of(const Polynomial<K>& that) const {
123if (dg <= 0) return Polynomial<K>(coef[0]);
124
125Polynomial<K> ans(K(0));
126for (long k=dg; k>=0; k--) {
127ans *= that;
128ans += Polynomial<K>(coef[k]);
129}
130return ans;
131}
132
133Polynomial<K> operator()(const Polynomial<K>& that) const {
134return of(that);
135}
136
137 // COMPARISON //
138
139bool operator==(const Polynomial<K>& that) const {
140if (dg != that.dg) return false;
141for (long k=0; k<=dg; k++) {
142if (coef[k] != that.coef[k]) return false;
143}
144return true;
145}
146
147bool operator!=(const Polynomial<K>& that) const {
148return !(*this == that);
149}
150
151 // ARITHMETIC //
152
153Polynomial<K> operator+(const Polynomial<K>& that) const {
154Polynomial<K> ans;
155long dmax = (dg > that.dg) ? dg : that.dg;
156ans.coef.resize(dmax+1);
157for (long k=0; k<=dmax; k++) {
158ans.coef[k] = get(k) + that.get(k);
159}
160ans.deg_check();
161return ans;
162}
163
164Polynomial<K> operator+=(const Polynomial<K>& that) {
165(*this) = (*this) + that;
166return *this;
167}
168
169Polynomial<K> operator-() const {
170Polynomial<K> ans;
Polynomials |
251 |
171ans.coef.resize(dg+1);
172for (long k=0; k<=dg; k++) ans.coef[k] = -coef[k];
173ans.deg_check();
174return ans;
175}
176
177Polynomial<K> operator-(const Polynomial<K>& that) const {
178Polynomial<K> ans;
179long dmax = (dg > that.dg) ? dg : that.dg;
180ans.coef.resize(dmax+1);
181for (long k=0; k<=dmax; k++) {
182ans.coef[k] = get(k) - that.get(k);
183}
184ans.deg_check();
185return ans;
186}
187
188Polynomial<K> operator-=(const Polynomial<K>& that) {
189(*this) = (*this) - that;
190return *this;
191}
192
193Polynomial<K> operator*(const Polynomial<K>& that) const {
194Polynomial<K> ans;
195if (isZero() || that.isZero()) return ans;
196long dans = dg + that.dg;
197ans.coef.resize(dans+1);
198for (long k=0; k<=dans; k++) {
199K c(0);
200for (long j=0; j<=k; j++) {
201if ((j<=dg) && (k-j<=that.dg)) c += coef[j]*that.coef[k-j];
202}
203ans.coef[k] = c;
204}
205ans.deg_check();
206return ans;
207}
208
209Polynomial<K> operator*=(const Polynomial<K>& that) {
210*this = (*this) * that;
211return *this;
212}
213
214Polynomial<K> pow(long k) const {
215if (k==0) return Polynomial<K>(1);
216if (k==1) return *this;
217
218if (k%2 == 0) {
219long half_k = k/2;
220Polynomial<K> ans;
221ans = (*this).pow(half_k);
222ans *= ans;
223return ans;
224}
225
226 long half_k = (k-1)/2;
252 |
C++ for Mathematicians |
227Polynomial<K> ans;
228ans = (*this).pow(half_k);
229ans *= ans;
230ans *= *this;
231return ans;
232}
233
234Polynomial<K> operator/(const Polynomial<K>& that) const {
235Polynomial<K> Q,R;
236quot_rem(*this, that, Q, R);
237return Q;
238}
239
240Polynomial<K> operator/=(const Polynomial<K>& that) {
241*this = (*this)/that;
242return *this;
243}
244
245Polynomial<K> operator%(const Polynomial<K>& that) const {
246Polynomial<K> Q,R;
247quot_rem(*this, that, Q, R);
248return R;
249}
250
251Polynomial<K> operator%=(const Polynomial<K>& that) {
252(*this) = (*this) % that;
253return *this;
254}
255
256void make_monic() {
257if (dg < 0) return;
258K lead = coef[dg];
259for (long j=0; j<=dg; j++) coef[j] /= lead;
260}
261
262bool is_monic() const {
263if (dg < 0) return false;
264return coef[dg] == K(1);
265}
266 |
|
|
267 |
}; |
// end of Polynomial<K> class template |
268 |
|
|
269template <class K>
270ostream& operator<<(ostream& os, const Polynomial<K>& P) {
271if (P.deg() <= 0) {
272os << "(" << P[0] << ")";
273return os;
274}
275for (long k=P.deg(); k>=0; k--) {
276if (P[k] != K(0)) {
277if (k < P.deg()) os << " + ";
278os << "(" << P[k] << ")";
279if (k>1) {
280os << "Xˆ" << k;
281continue;
282}
Polynomials |
253 |
283if (k==1) {
284os << "X";
285}
286}
287}
288return os;
289}
290
291template <class J, class K>
292Polynomial<K> operator+(J x, const Polynomial<K>& P)
293{ return P + K(x); }
294
295template <class J, class K>
296Polynomial<K> operator-(J x, const Polynomial<K>& P)
297{ return (-P) + K(x); }
298
299template <class J, class K>
300Polynomial<K> operator*(J x, const Polynomial<K>& P)
301{ return P * K(x); }
302
303template <class K>
304void quot_rem(const Polynomial<K>& A,
305 |
const Polynomial<K>& B, |
|
306 |
Polynomial<K>& Q, |
|
307 |
Polynomial<K>& R) |
{ |
308Q.clear();
309R.clear();
310
311 Polynomial<K> AA (A); // copy of A
312
313while (AA.deg() >= B.deg()) {
314long k = AA.deg()-B.deg();
315Polynomial<K> BB = B;
316BB.shift(k);
317K a_lead = AA[AA.deg()];
318K b_lead = BB[BB.deg()];
319for (long j=0; j <= BB.deg(); j++) BB.set(j, BB[j]*a_lead/b_lead);
320AA -= BB;
321Q.set(k,a_lead/b_lead);
322}
323R = A - Q*B;
324}
325
326template <class K>
327Polynomial<K> gcd(const Polynomial<K>& A, const Polynomial<K>& B) {
328if (B.isZero()) {
329if (A.is_monic()) return A;
330Polynomial<K> AA(A);
331AA.make_monic();
332return AA;
333}
334
335Polynomial<K> C;
336C = A%B;
337return gcd(B,C);
338 }
254 |
C++ for Mathematicians |
339
340template <class K>
341Polynomial<K> gcd(const Polynomial<K>& A,
342 |
const Polynomial<K>& B, |
|
343 |
Polynomial<K>& S, |
|
344 |
Polynomial<K>& T) |
{ |
345 |
Polynomial<K> D; // holds the answer |
|
346 |
|
|
347// If A and B are both 0, set S=T=0 and return 0.
348if (A.isZero() && B.isZero()) {
349S.clear();
350T.clear();
351return D;
352}
353
354// If A is not 0 but B is, D = A/a_lead, S = a_lead, T = 0
355if (B.isZero()) {
356D = A;
357K a_lead = A[A.deg()];
358D.make_monic();
359S = Polynomial<K>(K(1)/a_lead);
360T.clear();
361return D;
362}
363
364 // Neither A nor B is zero, so we recurse
365
366Polynomial<K> Q;
367Polynomial<K> R;
368quot_rem(A,B,Q,R);
369
370Polynomial<K> SS;
371Polynomial<K> TT;
372
373 D = gcd(B,R,SS,TT);
374
375S = TT;
376T = SS - Q*TT;
377
378return D;
379}
380
381#endif
12.4 The GCD problem revisited
In Chapter 3 we considered the question (phrased imprecisely here): What is the probability that two positive integers are relatively prime? We found that the answer is 1/ζ (2). Here we consider a similar problem: What is the probability that two
Polynomials |
255 |
randomly chosen polynomials are relatively prime?
To be more precise, let Bd denote the set of all polynomials in Z2[x] of degree less than d; there are 2d such polynomials ad−1xd−1 + ad−2xd−2 + ··· + a1x + a0
where the ajs are 0 or 1. Let pd denote the probability the two polynomials, chosen uniformly and independently from Bd are relatively prime. What can we say about pd as d → ∞?
To formulate a conjecture, we write a program to evaluate pd by direct enumeration. This is the approach we used in Section 3.5. With luck, modest values of d will lead us to the answer. The overall structure of the program is this:
1.Ask the user to input d.
2.Build an array containing all the polynomials in Bd .
3.For all i < j, count the number of times the ith and jth polynomials are relatively prime.
4.From this count, we learn the numerator of pd . Divide by 22d to find the answer.
Of these, the most difficult part is the construction of the list in step 2. To generate this table efficiently, we observe that there is a natural one-to-one correspondence between d-digit binary numbers and polynomials in Bd , illustrated here with d = 6.
000000 |
↔ |
0 |
000001 |
↔ |
1 |
000010 |
↔ |
x |
000011 |
↔ |
x + 1 |
000100 |
↔ |
x2 |
000101 |
↔ |
x2 + 1 |
|
. |
|
|
. |
|
|
. |
|
111111 |
↔ |
x5 + x4 + x3 + x2 + x + 1 |
Integer values are stored in computers in binary, so our first step is to write a procedure to convert integers into polynomials:
bd−1bd−2 ...b1b0 7→ bd−1xd−1 + bd−2xd−2 + ···+ b1x + b0 Bd
We call this procedure long2poly. Here is its header file.
Program 12.5: Header file long2poly.h.
1 #ifndef LONG_TO_POLY_H
2#define LONG_TO_POLY_H
3
4#include "Polynomial.h"