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

C++ For Mathematicians (2006) [eng]

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

426

C++ for Mathematicians

5.7 Here is a program.

#include <iostream> using namespace std;

int main() {

const int STEPS = 15; long a[STEPS];

long b[STEPS];

a[0] = b[0] = 1;

for (int k=1; k<STEPS; k++) { a[k] = b[k-1];

b[k] = a[k-1] + 2*b[k-1];

}

for (int k=0; k<STEPS; k++) {

cout << k << "\t" << a[k] << "\t" << b[k] << "\t" << double(a[k])/double(b[k]) << endl;

}

return 0;

}

Notice the use of a const int to declare the array sizes. If we decide to recompile this program to generate arrays of a different size, then we only need to edit this one value.

Here is the output of the program.

 

1

1

1

 

 

 

 

0

 

 

1

1

3

0.333333

 

 

2

3

7

0.428571

 

 

3

7

17

0.411765

 

 

4

17

41

0.414634

 

 

5

41

99

0.414141

 

 

6

99

239

0.414226

 

 

7

239

577

0.414211

 

 

8

577

1393

0.414214

 

 

9

1393

3363

0.414213

 

 

10

3363

8119

0.414214

 

 

11

8119

19601

0.414214

 

 

12

19601

47321

0.414214

 

 

13

47321

114243

0.414214

 

 

14

114243

275807

0.414214

 

 

 

 

 

 

 

 

Based on this chart, it is safe to conjecture that an/bn 2 1.

Here’s one approach to proving this. The recurrence can be written in matrix form as

a0

 

= 1

and

an =

0 1 an1 .

 

 

 

b0

 

 

1

 

 

 

bn

1 2 bn1

 

 

 

 

0 1

 

 

 

 

 

 

 

 

The eigenvalues of

are 1

±2 with corresponding eigenvectors 1±1

 

,

1 2

2

respectively. The iterates of this linear system diverge in the direction of the

Answers

 

 

 

427

eigenvector corresponding to the eigenvalue of largest magnitude, 1+

 

 

 

 

 

2; that

 

1+

 

 

 

 

 

 

 

is, they tend to infinity in the direction

2

, and so an/bn → −1

+ 2.

1

 

5.8 Here is such a procedure.

 

 

 

 

 

 

 

 

long max_value(const long* array, long nels) {

 

 

 

 

 

long ans;

 

 

 

 

 

 

 

 

ans = array[0];

 

 

 

 

 

 

 

 

for (long k=1; k<nels; k++) {

 

 

 

 

 

 

 

 

if (ans < array[k]) {

 

 

 

 

 

 

 

 

ans = array[k];

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

return ans;

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

Notice that the first argument is type const long*. The long* means that the argument is an array of long values. The const certifies that this procedure does not modify the values held in the array.

5.9Here is a procedure to generate an array of Fibonacci numbers as a return value.

long* make_fibs(long n) { long* ans;

ans = new long[n]; ans[0] = 1;

ans[1] = 1;

for (int k=2; k<n; k++) ans[k] = ans[k-1] + ans[k-2]; return ans;

}

Notice that the return type is long* because this procedure returns an array.

The problem with the main() given in the problem is that it suffers a memory leak. The array created by make_fibs is never released with a delete[] statement.

The use of new in this design is unavoidable. A better strategy is to design a procedure with type void make_fibs(long n, long* array);. It is then the responsibility of the programmer to allocate an array of the appropriate size before invoking this procedure:

long* fibs;

fibs = new long[20]; make_fibs(20,fibs); // ...

delete[] fibs;

alternatively:

long fibs[20]; make_fibs(20,fibs); //...

428

C++ for Mathematicians

No delete[] is necessary in this second instance because fibs was not allocated with new.

5.10 Here is such a procedure.

long fibs(int n) {

const int MAX_ARG = 40; static bool first_time = true; static long values[MAX_ARG+1];

if (first_time) { values[0] = 1; values[1] = 1;

for (int k=2; k<=MAX_ARG; k++) { values[k] = values[k-1] + values[k-2];

}

first_time = false;

}

if ((n<0) || (n>MAX_ARG)) return -1; return values[n];

}

The key idea is the use of static variables. The Boolean first_time is initialized to true so the code can detect when it is first run. The array values is then populated with Fibonacci numbers and first_time is set to false so the initialization is not repeated on subsequent calls. The array values is also declared to be static so that its contents persist between invocations.

5.11 Here is such a program.

#include <iostream> using namespace std;

int main() {

long size = 10000000; // ten million long* array;

long count = 0; while (true) {

array = new long[size]; ++count;

cout << "Success with array #" << count << endl;

}

}

 

When this program is run on my computer, we see the following output.

 

 

 

 

 

Success with array #1

 

 

 

 

 

 

Success with array #2

 

 

 

Success with array #3

 

 

 

Success with array #4

 

 

...

 

 

 

Success with array #90

 

 

 

Success with array #91

 

 

 

*** malloc: vm_allocate(size=40001536) failed (error code=3)

 

 

 

Answers

429

 

 

*** malloc[4998]: error: Can’t allocate region

 

 

 

 

 

 

Abort trap

 

 

 

 

 

5.12Enter the digits (separated by spaces) into the On-Line Encyclopedia of Integer

Sequences. It responds that this is sequence A059742 giving the decimal digits of π + e.

6.1Here is a header file for the Line class.

#ifndef LINE_H #define LINE_H

#include "Point.h"

class Line { private:

double a,b,c; // to specify the line ax+by+c=0

public:

//constructors Line();

Line(Point P, Point Q);

Line(double aa, double bb, double cc);

//get methods

double getA() const; double getB() const; double getC() const;

//reflection methods void reflectX(); void reflectY();

//check if a Point is on this Line bool incident(Point P) const;

//generate a Point on this Line Point find_Point() const;

//check if this Line equals another bool operator==(const Line& that) const;

};

// write a Line to an output stream

ostream& operator<<(ostream& os, const Line& L);

// find the distance between a Point and a Line double dist(Line L, Point P);

double dist(Point P, Line L);

#endif

And here is the code file Line.cc.

#include "Line.h"

430 C++ for Mathematicians

Line::Line() {

a = b = c = 0.;

}

Line::Line(Point P, Point Q) {

//get the coordinates of the points double x1 = P.getX();

double y1 = P.getY(); double x2 = Q.getX(); double y2 = Q.getY();

//If the points are the same, make the Line horizontal

//through the one point.

if (P==Q)

{

cerr <<

"Warning: Constructing Line from two equal Points"

<<

endl;

a = 0.;

 

b = 1.;

 

c = -y1;

 

return;

 

}

 

//If the points are distinct, we continue starting

//with the special case of a vertical line

if (x1 == x2) { a = 1.;

b = 0.; c = -x1; return;

}

// The points are distinct and not vertical a = y2 - y1;

b = x1 - x2;

c = x2*y1 - x1*y2;

}

Line::Line(double aa, double bb, double cc) { // Check if the data are valid

if ((aa==0.) && (bb==0.)) {

cerr << "Warning: Invalid call to Line(aa,bb,cc)" << endl; a = 0.;

b = 1.; c = cc; return;

}

a = aa; b = bb; c = cc;

}

// get methods

double Line::getA() const { return a; } double Line::getB() const { return b; }

Answers

431

double Line::getC() const { return c; }

// reflection methods

void Line::reflectX() { b = -b; } void Line::reflectY() { a = -a; }

//check if a Point is on this Line bool Line::incident(Point P) const {

return a*P.getX() + b*P.getY() + c == 0. ;

}

//generate a Point on a Line

Point Line::find_Point() const {

//if it’s a vertical line, return x-intercept if (b==0.) {

return Point(-c/a, 0);

}

//otherwise, return the x-intercept

return Point(0,-c/b);

}

bool Line::operator==(const Line& that) const {

//Special case when a == 0 if (a==0) {

if (that.a != 0) return false; return c/b == that.c/that.b;

}

//When a is not 0

if (that.a == 0) return false;

return (b/a == that.b/that.a) && (c/a == that.c/that.a);

}

// for writing to an output stream

ostream& operator<<(ostream& os, const Line& L) { os << "[" << L.getA() << "," << L.getB() << ","

<< L.getC() << "]"; return os;

}

// determine the distance from a Point to a Line double dist(Line L, Point P) {

//fetch a,b,c for the line double a = L.getA(); double b = L.getB(); double c = L.getC();

//fetch the coordinates of the point double x = P.getX();

double y = P.getY();

//normalize by dividing by sqrt(a*a+b*b) double d = sqrt(a*a + b*b);

a /= d; b /= d; c /= d;

432

C++ for Mathematicians

// project P onto the unit vector (a,b) and add c double ans = x*a + y*b + c;

return fabs(ans);

}

double dist(Point P, Line L) { return dist(L,P); }

6.2It is natural to suspect that something is wrong with the logic and algebra used to create the procedures, but that is not the case. If we examine the [a,b,c] triples for the two lines, we see that the triple for L is a scalar multiple of the triple for M (the factor is 1.4), so these represent the same line.

The problem is roundoff. Indeed, if we print the distance from Y to M the computer responds 8.88178e-16. Minute errors in the divisions cause the problems; note that the slope of the line is 5/7 and the quotient cannot be held exactly in a double variable.

There is no perfect solution to this problem. A good strategy is to replace exact equality tests with |x y| < ε where ε is either built into the code or is set by the user.

6.3The modification to Line.h is modest. The old private section would be replaced by this:

private: Point A; Point B;

Nothing else in Line.h would need to be modified.

Extensive repairs to Line.cc are now needed. For example, the get methods would need to be completely reworked.

However, code that uses the Line class would not require any modification at all!

6.4 A good way to do this is with a procedure declared like this:

bool intersect(Line L, Line M, Point& P);

The input Line objects are L and M. If they are parallel, then the procedure should return false (to mean do not intersect) and leave P unaffected. Otherwise, we set P to be the Point of intersection and return true.

6.5It is not unreasonable to make the end points of a LineSegment object be public data elements because if a program modifies these data directly (and not through a set method), there is no possibility of creating an invalid object (we allow the two end points to be the same). By contrast, were we to allow open access to a Line object’s data, an unwary user might set both a and b to 0 creating an invalid Line object.

Answers

433

In general, it is better to keep the data hidden and write simple get and set methods to manipulate those data.

Here is a header file LineSegment.h. Writing the LineSegment.cc file is left to you.

#ifndef LINE_SEGMENT_H #define LINE_SEGMENT_H

#include <iostream> #include "Point.h" using namespace std;

class LineSegment { private:

// the end points of the segment Point A;

Point B;

public:

//constructors LineSegment(); LineSegment(Point P, Point Q);

LineSegment(double x1, double y1, double x2, double y2);

//get & set methods

Point getA() const; Point getB() const; void setA(Point P); void setB(Point Q);

//Length and midpoint methods double length() const;

Point midpoint() const;

//Check for equality

bool operator==(const Point& that);

};

// for output

ostream& operator<<(ostream& os, const LineSegment& LS); #endif

6.6Here are two solutions. In both cases, updating the fields x and y is straightforward. The key issue is: how do we write the return statement at the end of the procedure.

In this first solution, we make a copy of the Point and return that:

Point Point::translate(double dx, double dy) { x += dx;

y += dy;

return Point(x,y);

}

434

C++ for Mathematicians

This code is clear but suffers a minor inefficiency because it makes a copy of the Point and then that copy gets sent back as a return value. We can make this more efficient by returning the Point object itself. Here is how that is done.

Point Point::translate(double dx, double dy) { x += dx;

y += dy; return *this;

}

The object *this is the Point object itself, so no extra copy is needed.

In this simple example, the inefficiency is minimal. However, if this method were to be used extensively then the change from return Point(x,y); to return *this; can make a noticeable difference.

6.7This is a subtle problem. Suppose we define a procedure proc with one argument. There are two ways we can declare this procedure:

Point proc(Point Q);

Point proc(Point &Q);

If we use the first syntax, then a copy of the argument is sent to the procedure, but if we use the second syntax, then the argument itself is sent. That is, when we call proc(W) the first version cannot modify W, but the second version can.

Similarly, the normal behavior of return X is to send back a copy of X. However, if we want to return the object X itself, we need to add an & to the return type in the procedure declaration.

For the problem at hand, we need to change the return type of the translate method from Point to Point&. In the public section in Point.h we declare the procedure like this.

Point& translate(double dx, double dy);

In Point.cc we have the following code.

Point& Point::translate(double dx,double dy) { x += dx;

y += dy; return *this;

}

Let’s examine how the old versions (from the previous problem) and this new version behave for the code (P.translate(1,2)).translate(10,10);.

In either old version P.translate(1,2) returns a copy of P. (The first old version returns a copy of a copy of P!) So the second invocation of translate acts on a copy of P and not the object P itself. Therefore, P is not modified by translate(10,10).

Answers

435

However, in the new version, the statement return *this; returns the object itself and not a copy. Therefore the second translate(10,10) acts on P and not on some unnamed copy of P.

One last point: In the solution to Exercise 6.6, both return Point(x,y); and return *this; have the intended effect of returning a copy of the Point. In this exercise, the code return Point(x,y); is inappropriate. We do not want to return a copy of the Point; we want to return the Point itself. So we must use return *this;.

7.1Here is the file Interval.h. Note that the keyword inline is optional for the class methods but mandatory for the operator<< procedure. We require both

#include <iostream> and using namespace std; to use ostream objects.

#ifndef INTERVAL_H #define INTERVAL_H #include <iostream> using namespace std;

class Interval { private:

double a,b; // end points of [a,b]

public: Interval() {

a = 0.; b = 1.;

}

Interval(double x, double y) { if (x<y) {

a = x; b = y;

}

else {

a = y; b = x;

}

}

double getA() const { return a; } double getB() const { return b; }

bool operator==(const Interval& that) const { return (a==that.a) && (b==that.b);

}

bool operator!=(const Interval& that) const { return !(*this == that);

}

bool operator<(const Interval& that) const { if (a < that.a) return true;

if (a > that.a) return false;