C++ For Mathematicians (2006) [eng]
.pdf476 |
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 |
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;