Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C++ For Mathematicians (2006) [eng]

.pdf
Скачиваний:
193
Добавлен:
16.08.2013
Размер:
31.64 Mб
Скачать

246

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"