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

C++ For Mathematicians (2006) [eng]

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

466

C++ for Mathematicians

single-argument constructor). However, a separate Point+Path procedure is necessary.

#ifndef PATH_H #define PATH_H #include "Point.h" #include <vector>

class Path { private:

vector<Point> pts; long npts;

public:

Path() { npts = 0; } Path(const Point& P) {

npts = 1; pts.resize(1); pts[0] = P;

}

long size() const { return npts; } Path operator+(const Path& that) const; Point operator[](int k) const;

};

Path operator+(const Point& X, const Path& P); ostream& operator<<(ostream& os, const Path& P);

#endif

#include "Path.h"

Path Path::operator+(const Path& that) const { Path ans;

ans.npts = npts + that.npts; ans.pts.resize(ans.npts);

for (int k=0; k<npts; k++) ans.pts[k] = pts[k];

for (int k=0; k<that.npts; k++) ans.pts[k+npts] = that.pts[k];

return ans;

}

Point Path::operator[](int k) const { if (npts==0) return Point(0,0);

k %= npts;

if (k<0) k = -k; return pts[k];

}

Path operator+(const Point& X, const Path& P) { return Path(X) + P;

}

ostream& operator<<(ostream& os, const Path& P) { os << "[ ";

for (int k=0; k<P.size(); k++) os << P[k] << " ";

Answers

467

os << "]"; return os;

}

12.1The code is a mildly edited version of the answer to Exercise 7.3; it works for any type for which the < is defined. Here is the file median.h.

#ifndef MEDIAN_H #define MEDIAN_H #include <algorithm> using namespace std;

template <class T>

T median(const T* array, long nels) { if (nels < 0) return 0.;

if (nels == 0) return array[0];

T* copy_array; copy_array = new T[nels];

for (int k=0; k<nels; k++) copy_array[k] = array[k];

sort(copy_array, copy_array+nels);

T ans;

ans = copy_array[nels/2]; delete[] copy_array; return ans;

}

#endif

There is an interesting difference between the algorithm we use here and the algorithm used in our solution to Exercise 7.3. In the first version, if the array contains an even number of elements, we return the average to the two central values. However, this new version might be applied to a type for which addition and division by 2 is not defined. Our solution is to return element numbern/2 of (the sorted copy of) an array with n elements.

12.2 Here is the new version of SmartArray.h.

#ifndef SMART_ARRAY_H #define SMART_ARRAY_H

template <class T> class SmartArray { private:

long N; T* data;

long adjust_index(long k) { k %= N;

if (k<0) k += N; return k;

}

public:

SmartArray(long nels = 1) {

p(x) gcd(p(x), p (x))

468

C++ for Mathematicians

N = (nels > 1) ? nels : 1; data = new T[N];

}

˜SmartArray() { delete[] data; } long get_N() const { return N; } T& operator[](long k) {

long kk = adjust_index(k); return data[kk];

}

};

#endif

12.3 Here is a file derivative.h that creates a template procedure derivative.

#ifndef DERIVATIVE_H #define DERIVATIVE_H #include "Polynomial.h"

template <class T>

Polynomial<T> derivative(const Polynomial<T>& P) { Polynomial<T> ans;

for (long k=1; k<=P.deg(); k++) { ans.set(k-1,k*P[k]);

}

return ans;

}

#endif

Alternatively, we could add a derivative() method to the Polynomial class template.

12.4 Here is some advice on handling multiple roots. If p(x) has multiple roots, then

q(x) =

has the same roots as p(x), but all the roots are simple.

12.5Here is a file triple.h that defines the triple class and the make_triple procedure templates.

#ifndef TRIPLE_H #define TRIPLE_H

template <class A, class B, class C> class triple {

public:

Afirst;

Bsecond;

Cthird;

triple() {};

triple(const A& x, const B& y, const C& z) {

Answers

469

first = x; second = y; third = z;

}

bool operator<(const triple& that) const { if (first < that.first) return true;

if (first > that.first) return false; if (second < that.second) return true; if (second > that.second) return false; if (third < that. third) return true; return false;

}

};

template <class A, class B, class C>

triple<A,B,C> make_triple(const A& x, const B& y, const C& z) { return triple<A,B,C>(x,y,z);

}

#endif

12.6Use the Polynomial template created in this chapter as a basic building block. Your template should start like this:

template <class T> class RationalFunction { private:

Polynomial<T> numerator; Polynomial<T> denominator;

public:

....

};

12.7Notice that in the following program we do not use C++’s set containers. The print_set procedure takes an integer, examines each of its bits, and prints out a set element when it finds a nonzero bit.

#include <iostream> using namespace std;

int LIMIT = 8*sizeof(unsigned long);

void print_set(unsigned long x) { cout << "{ ";

for (int k=0; k<LIMIT; k++) { unsigned long mask = 1<<k;

if ((x&mask) != 0) cout << k+1 << " ";

}

cout << "}" << endl;

}

int main() {

cout << "Enter n (up to " << LIMIT-1 << ") --> "; int n;

cin >> n;

470

C++ for Mathematicians

if ((n<0)

|| (n>=LIMIT)) {

cerr <<

"Please use a positive value that is less than "

<<

LIMIT << endl;

return -1;

}

unsigned long top = (1<<n) - 1;

for (unsigned long x = 0 ; x <= top; x++) { print_set(x);

}

return 0;

}

Here is a sample run.

 

 

Enter n (up to 31) --> 3

{}

{1 }

{2 }

{1 2 }

{3 }

{1 3 }

{2 3 }

{1 2 3 }

 

 

13.1Of course, the “right” way to solve this problem is to see that there are 24 factors of 5 in 100!. Here is a solution that uses the GMP package.

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

mpz_class factorial(long n) { mpz_class ans(1);

for (int k=2; k<=n; k++) ans *= k; return ans;

}

int main() {

mpz_class value = factorial(100); int count = 0;

while (value % 10 == 0) { value /= 10;

count++;

}

cout << count << endl; return 0;

}

13.2 Here is the program.

#include <iostream> using namespace std;

const int NROWS = 21;

Answers

471

int main() {

int table[NROWS][NROWS];

table[0][0] = 1;

for (int k=1; k<NROWS; k++) table[0][k] = 0;

for (int n=1; n<NROWS; n++) { table[n][0] = 1; table[n][n] = 1;

for (int k=1; k<n; k++)

table[n][k] = table[n-1][k-1]+table[n-1][k]; for (int k=n+1; k<NROWS; k++) table[n][k] = 0;

}

for (int n=0; n<NROWS; n++) { for (int k=0; k<NROWS; k++) {

cout << table[n][k] << "\t";

}

cout << endl;

}

return 0;

}

To extend this to a 100 × 100 table, it is not enough to change the value of NROWS. The binomial coefficients grow too large to fit in long variables. Even long long variables are not big enough (assuming that these are 64 bits). Instead, we need to use arbitrary precision integers (e.g., from the GMP package) or settle for decimal approximations (i.e., use a double table).

14.1Here are two solutions. First, we can convert the string into a char* array, and then use atoi:

int n;

n = atoi(s.c_str());

Alternatively, we can wrap the string inside an istringstream and then extract the integer value using the >> operator:

istringstream is(s); int n;

is >> n;

14.2ifstream fin(file_name.cstr());

14.3The addition of a char and an int results in an int, and so instead of writing the letters a through z, instead we see the ASCII values for these letters.

The solution is to replace ’a’+k with char(’a’+k).

14.4Here are the header and code files, Free.h and Free.cc, that implement the Free class. Note that we include constructors for building Free objects from a single character, a string, and a char* array. The helper methods named

472

C++ for Mathematicians

reduce are used to cancel adjacent inverse elements. The clear_check method is used by the constructor to prevent the inclusion of improper characters.

#ifndef FREE_H #define FREE_H #include <iostream> #include <cctype> using namespace std;

class Free { private:

string word;

bool reduce(int k); void reduce(); void check_clear();

public:

Free() { word.clear(); } Free(char ch) {

word = string(" "); word[0]=ch; check_clear();

}

Free(const string& S) { word = S; check_clear();

}

Free(const char* str) { word = string(str); check_clear();

}

string toString() const { return word; } Free operator*(const Free& that) const {

Free ans;

ans.word = word + that.word; ans.reduce();

return ans;

}

Free inverse() const;

};

inline

ostream& operator<<(ostream &os, const Free& f) { os << ’(’ << f.toString() << ’)’;

return os;

}

#endif

#include "Free.h"

void Free::check_clear() {

for (unsigned k=0; k<word.size(); k++) { while (!isalpha(word[k])) {

word.erase(k,1);

}

Answers

473

}

reduce();

}

bool Free::reduce(int k) { char c1 = word[k];

char c2 = word[k+1];

if ((islower(c1)&&isupper(c2))||(isupper(c1)&&islower(c2))){ if (toupper(c1) == toupper(c2)) {

word.erase(k,2); return true;

}

}

return false;

}

void Free::reduce() {

if (word.size() < 2) return;

for (int k=0; k<int(word.size())-1; k++) { while (reduce(k)) {

if (k>0) --k;

}

}

}

Free Free::inverse() const { Free ans;

ans.word = word;

int n = int(word.size()); for (int k=0; k<n; k++) {

char ch = word[k];

ch = isupper(ch) ? tolower(ch) : toupper(ch); ans.word[n-1-k] = ch;

}

return ans;

}

14.5The following program reads a text file (given as a command-line argument) and reports letter frequencies.

#include <iostream> #include <fstream> using namespace std;

int main(int argc, char** argv) { ifstream fin;

if (argc < 2) {

cerr << "Usage: " << argv[0] << " filename" << endl; return 1;

}

fin.open(argv[1]); if (fin.fail()) {

cerr << "Unable to open " << argv[1] << " for reading" << endl;

return 2;

}

474 C++ for Mathematicians

string word; long count[26]; long total = 0;

for (int k=0; k<26; k++) count[k] = 0;

while (true) { fin >> word;

if (fin.fail()) break;

for (int k=0; k<word.size(); k++) { char ch = tolower(word[k]);

if (isalpha(ch)) { count[ch-’a’]++; total++;

}

}

}

for (char ch=’a’; ch <= ’z’; ch++) { int idx = ch - ’a’;

cout << ch << "\t" << 100*double(count[idx])/double(total) << endl;

}

return 0;

}

We ran this code on four 19th-century texts; three of these were in English and one in French. The results are in the accompanying table. It’s not hard to distinguish one from the other three.

14.6A complete program to count polygrams in a text file is included on the accompanying CD-ROM.

14.7The following program uses the Point class from Chapter 6 (files Point.h and Point.cc) plus the files Koch.h, Koch.cc, and main.cc that we present here.

#ifndef KOCH_H #define KOCH_H

#include <vector> #include <cmath> #include "Point.h"

using namespace std;

class Koch {

private: vector<Point> pts;

public:

Koch(int n=0);

int size() const { return pts.size(); }

Answers

475

Letter frequency chart to accompany solution to Exercise 14.5.

 

Letter

Frequency (percent)

 

 

a

8.4

8.7

8.1

8.3

 

 

b

1.5

1.2

1.4

1.5

 

 

c

2.6

3.1

2.6

2.4

 

 

d

4.4

3.8

4.4

4.8

 

 

e

12.4

14.9

12.6

12.5

 

 

f

2.4

1.1

2.4

2.3

 

 

g

2.4

1.0

2.2

2.4

 

 

h

6.2

1.2

5.6

6.0

 

 

i

6.6

7.6

7.2

6.8

 

 

j

0.1

0.4

0.1

0.1

 

 

k

0.6

0.0

0.8

0.8

 

 

l

3.7

6.6

4.3

3.8

 

 

m

2.4

3.0

2.8

2.6

 

 

n

6.7

6.8

7.0

7.2

 

 

o

7.5

5.5

7.1

7.1

 

 

p

2.2

2.7

1.8

1.8

 

 

q

0.1

1.1

0.1

0.1

 

 

r

5.9

6.7

5.7

6.0

 

 

s

6.5

8.3

5.9

6.0

 

 

t

9.2

7.6

9.6

9.7

 

 

u

2.9

6.3

2.8

2.7

 

 

v

1.0

1.6

0.9

0.9

 

 

w

2.2

0.1

2.2

2.3

 

 

x

0.3

0.4

0.2

0.1

 

 

y

1.7

0.4

1.9

1.8

 

 

z

0.0

0.1

0.1

0.0

 

 

 

 

 

 

 

 

Point get(int k) const { return pts[k]; }

};

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

#endif

#include "Koch.h"

Koch::Koch(int n) { if (n<0) n=0;

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

for (int k=0; k<n; k++) { vector<Point> tmp;

for (unsigned j=0; j<pts.size()-1; j++) { Point P = pts[j];

Point Q = pts[j+1];