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

C++ For Mathematicians (2006) [eng]

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

476

C++ for Mathematicians

Point X,Y,Z; koch_step(P,Q,X,Y,Z); tmp.push_back(P); tmp.push_back(X); tmp.push_back(Y); tmp.push_back(Z);

}

tmp.push_back(Point(1,0)); pts = tmp;

}

}

const double theta = M_PI/3;

void koch_step(const Point& P, const Point& Q, Point& X, Point& Y, Point& Z) {

double a = P.getX(); double b = P.getY(); double c = Q.getX(); double d = Q.getY();

X = Point( (2*a+c)/3, (2*b+d)/3 );

Z = Point( (a+2*c)/3, (b+2*d)/3 );

double x = (c-a)/3; double y = (d-b)/3;

double xx = cos(theta)*x - sin(theta)*y; double yy = sin(theta)*x + cos(theta)*y;

Y = Point( X.getX() + xx, X.getY() + yy);

}

#include "Koch.h" #include "plotter.h" #include <fstream> using namespace std;

const char* OUTPUT_FILE = "koch.eps";

int main() { int n;

cout << "Enter depth: "; cin >> n;

Koch K(n); PlotterParams params;

ofstream pout(OUTPUT_FILE); PSPlotter P(cin,pout,cerr,params);

if (P.openpl() < 0) {

cerr << "Unable to open plotter for file " << OUTPUT_FILE << endl;

return 1;

}

P.fspace(0.,0.,1.,1.);

Answers

477

for (int k=0; k<K.size()-1; k++) { double x1 = K.get(k).getX(); double y1 = K.get(k).getY(); double x2 = K.get(k+1).getX(); double y2 = K.get(k+1).getY(); P.fline(x1,y1,x2,y2);

}

P.closepl(); return 0;

}

When run at depth 5, the following image is produced.

14.8 Here is a program to draw the Mandelbrot set.

#include <iostream> #include <fstream> #include <complex> #include "plotter.h" using namespace std;

const int MAX_ITS = 500; const int PIXELS = 1000;

const char* FILE_NAME = "mandelbrot.gif";

double norm(complex<double> z) { double a = z.real();

double b = z.imag(); return a*a + b*b;

}

bool in_set(complex<double> c) { complex<double> z = 0.;

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

z = z*z + c;

if (norm(z) > 4.) return false;

}

return true;

}

complex<double> pix_to_complex(int i, int j) { double a = (4.2*i)/PIXELS - 2.1;

double b = (4.2*j)/PIXELS - 2.1; return complex<double>(a,b);

}

478 C++ for Mathematicians

int main() { PlotterParams params;

ofstream pout(FILE_NAME);

GIFPlotter P(cin, pout, cerr, params); if (P.openpl() < 0) {

cerr << "Unable to open plotter for file " << FILE_NAME << endl;

return 1;

}

P.space(0,0,PIXELS,PIXELS);

P.pencolorname("black");

for (int i=0; i<PIXELS; i++) { for (int j=0; j<PIXELS; j++) {

complex<double> z = pix_to_complex(i,j); if (in_set(z)) {

P.point(i,j);

}

}

}

P.closepl(); return 0;

}

It produces the following image.

15.1 Here is Card.h with all methods and procedures inline.

#ifndef CARD_H #define CARD_H

#include <vector> #include <string> #include <iostream> using namespace std;

const int ACE = 1; const int JACK = 11; const int QUEEN = 12;

Answers

479

const int KING = 13;

 

const string value_names[] = { "nil", "ace", "two", "three",

 

"four", "five", "six", "seven",

"eight", "nine", "ten",

 

"jack", "queen", "king" };

 

const string suit_names[] = { "clubs", "diamonds", "hearts", "spades" };

enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES };

class Card { private:

int value; Suit suit;

int card2int() const { return 13*suit + value;

}

public:

Card() { value = ACE; suit = SPADES; } Card(int spots, Suit family) {

spots %= 13;

if (spots <= 0) spots += 13; value = spots;

suit = family;

}

bool operator<(const Card& that) const { return card2int() < that.card2int();

}

bool operator==(const Card& that) const { return card2int() == that.card2int();

}

bool operator!=(const Card& that) const { return card2int() != that.card2int();

}

friend ostream& operator<<(ostream& os, const Card& C);

};

inline ostream& operator<<(ostream& os, const Card& C) {

 

os << value_names[C.value] << " of "

 

<< suit_names[C.suit];

 

return os;

 

}

 

#endif

15.2

The statement T* xp = new T(10); creates a single new object of type T

 

in which the value 10 is supplied to the class T constructor.

 

The statement T* xp = new T[10]; creates a new array to hold 10 objects

 

of type T.

15.3

Notice that in the following program we catch a bad_alloc that is thrown by

480

C++ for Mathematicians

new when there is not enough memory to allocate. (The prefix std:: is not necessary because we declared using namespace std;.)

#include <iostream> using namespace std;

int main() {

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

long count = 0; while (true) {

try {

array = new long[size];

}

catch (bad_alloc X) {

cerr << "Unable to allocate memory" << endl; exit(1);

}

++count;

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

}

}

15.4+. x+y addition or string concatenation, x++ and ++x increment, and unary +x is the identity function (not mentioned elsewhere in this book).

-. a-b subtraction, -x unary minus, x-- and --x decrement, and combined with > to give -> arrow operator.

*. a*b multiplication, *x pointer/iterator dereferencing, type* var pointer/array declaration, and combined with / to delimit comments: /* and */.

/. a/b division and// single line comment. See also *.

=. a=b assignment, a==b equality testing, combined with < and > for and , and combined with ! for inequality testing.

<. a<b for less than, a<<b for bit shift and stream output, and used with > for declaring templates and for #include of standard headers. See also =.

>. a>b for greater than, a>>b for bit shift and stream input. See also =, -, and

>.

!. !a Boolean not. See also =.

&. a&b bitwise and, &a address of, type& var reference declaration, a&&b Boolean and.

|. a|b bitwise or and a||b Boolean or.

˜. ˜a bitwise not and ˜Class destructor.

:. class : public subclass inheritance, label: statement; labeling, case 1: label in a switch statement,

Child(argument_list) : Parent(argument_sublist) { ... }

Answers

481

parent class constructor invocation, Class::name/namespace::name scope resolution, and a?b:c in the trigraph operator.

15.5This is a tricky problem combining many of the concepts we have encountered in this book.

Each constructible number can be represented as an expression tree. For ex-

ample, the golden mean (1 +

5)/2 can be parsed like this.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

+

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

 

5

2

There are three types of nodes in this tree:

 

 

 

Atomic nodes that contain integers.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Unary operations nodes (

 

 

and unary ) that have one child.

Binary operation nodes (+, binary , ×, and ÷) that have a left and a right child.

Each child node is, itself, a constructible number.

Our solution begins by defining the data elements of the Constructible class. They are:

An operation code op. This field indicates the type of node: an atomic node (i.e., an integer), a unary operation, or a binary operation. To this end we define an enum type called Operation that is one of NOP (for an atomic node), PLUS, MINUS, TIMES, DIVIDE, NEG, or SQRT.

An integer val field. This is only used in the case where this is an atomic node.

An integer nargs field. This is 0 for an atomic node, 1 for a unary operation, or 2 for a binary operation. It tell us how many direct children this node has.

An array args containing the children. This array is dynamically allocated and its size is given in nargs.

The constructor creates an atomic node with a given integer value. The various operator methods create Constructible objects with one or two arguments (saved in the args array). An assignment operator is needed when copying a Constructible object so that a true independent copy is created. Because various operations allocate storage (via new), we need to be mindful to release that storage (e.g., in the destructor).

482

C++ for Mathematicians

The code for the binary operator methods are remarkably similar, so we write a private helper method named binary_op. A similar unary_op is provided for the negation and square root.

The procedure to produce a decimal (complex) value for a Constructible object works recursively on the structure of the object. Note that the Standard Template Library does not define a square root method for complex<double> values, so we write our own.

The header Constructible.h and code files Constructible.cc follow.

#ifndef CONSTRUCTIBLE_H #define CONSTRUCTIBLE_H #include <iostream> #include <complex> using namespace std;

enum Operation { NOP, PLUS, MINUS, TIMES, DIVIDE, NEG, SQRT };

class Constructible { private:

Operation op; long val;

int nargs; Constructible *args;

Constructible binary_op(Operation verb,

const Constructible& that) const; Constructible unary_op(Operation verb) const;

public:

Constructible(long x = 0) { op = NOP; val = x; nargs = 0; } ˜Constructible() {

if (nargs > 0) { delete[] args;

}

}

Constructible(const Constructible& X); Constructible& operator=(const Constructible& that);

Constructible operator+(const Constructible& that) const { return binary_op(PLUS,that);

}

Constructible operator-() const { return unary_op(NEG); } Constructible operator-(const Constructible& that) const {

return binary_op(MINUS,that);

}

Constructible operator*(const Constructible& that) const { return binary_op(TIMES,that);

}

Constructible operator/(const Constructible& that) const { return binary_op(DIVIDE, that);

}

Constructible sqrt() const { return unary_op(SQRT); } complex<double> value() const;

friend ostream& operator<<(ostream& os,

const Constructible& X);

};

Answers

483

inline Constructible operator+(long a, Constructible X) { return Constructible(a) + X;

}

inline Constructible operator*(long a, Constructible X) { return Constructible(a) * X;

}

inline Constructible operator-(long a, Constructible X) { return Constructible(a) - X;

}

inline Constructible operator/(long a, Constructible X) { return Constructible(a) / X;

}

complex<double> complex_sqrt(const complex<double>& z);

inline Constructible sqrt(const Constructible& X) { return X.sqrt();

}

#endif

#include "Constructible.h" #include <cmath>

Constructible::Constructible(const Constructible& X) { op = X.op;

val = X.val; nargs = X.nargs; if (nargs > 0) {

args = new Constructible[nargs]; for (int k=0; k<nargs; k++) {

args[k] = X.args[k];

}

}

}

Constructible&

Constructible::operator=(const Constructible& that) { op = that.op;

val = that.val; if (nargs > 0) { delete[] args;

}

nargs = that.nargs; if (nargs > 0) {

args = new Constructible[nargs]; for (int k=0; k<nargs; k++) {

args[k] = that.args[k];

}

}

return *this;

}

Constructible Constructible::binary_op(Operation verb,

const Constructible& that) const {

484

C++ for Mathematicians

 

Constructible ans;

 

ans.op = verb;

 

ans.nargs = 2;

 

ans.args = new Constructible[2];

ans.args[0] = *this; ans.args[1] = that; return ans;

}

Constructible Constructible::unary_op(Operation verb) const {

Constructible ans; ans.op = verb; ans.nargs = 1;

ans.args = new Constructible[1]; ans.args[0] = *this;

return ans;

}

complex<double> Constructible::value() const { switch(op) {

case NOP:

return complex<double>(double(val),0); break;

case PLUS:

return args[0].value() + args[1].value(); break;

case MINUS:

return args[0].value() - args[1].value(); break;

case NEG:

return -args[0].value(); break;

case TIMES:

return args[0].value() * args[1].value(); break;

case DIVIDE:

return args[0].value() / args[1].value(); break;

case SQRT:

return complex_sqrt(args[0].value()); break;

default:

cerr << "This shouldn’t happen!" << endl;

}

return complex<double>(0);

}

const char* OPEN = "\\left("; const char* CLOSE = "\\right)";

ostream& operator<<(ostream& os, const Constructible& X) { switch(X.op) {

case NOP:

os << X.val; break;

Answers

485

case PLUS:

os << OPEN << X.args[0] << " + " << X.args[1] << CLOSE; break;

case MINUS:

os << OPEN << X.args[0] << " - " << X.args[1] << CLOSE; break;

case TIMES:

os << OPEN << X.args[0] << " \\times " << X.args[1] << CLOSE;

break; case DIVIDE:

os << "\\frac{" << X.args[0] << "}{" << X.args[1] << "}"; break;

case NEG:

os << "-" << OPEN << X.args[0] << CLOSE; break;

case SQRT:

os << "\\sqrt{" << X.args[0] << "}"; break;

default:

os << "INVALID CONSTRUCTIBLE OBJECT";

}

return os;

}

complex<double> complex_sqrt(const complex<double>& z) { double x = z.real();

double y = z.imag();

if ((x==0.) && (y==0.)) return complex<double>(0.); if (y==0.) {

if (x >= 0.) {

return complex<double>(sqrt(x),0);

}

else {

return complex<double>(0., sqrt(-x));

}

}

double r = sqrt(hypot(x,y)); double t = atan2(y,x)/2.;

return complex<double> (r*cos(t), r*sin(t));

}

The following brief main() illustrates the use of the Constructible class.

#include "Constructible.h" int main() {

Constructible five(5);

Constructible PHI = (1+sqrt(five))/2;

cout << "PHI = " << PHI.value().real() << endl; cout << "TeX form: " << PHI << endl;

return 0;

}

This gives the following output.