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

C++ For Mathematicians (2006) [eng]

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

436

C++ for Mathematicians

return b < that.b;

}

};

inline ostream& operator<<(ostream& os, const Interval& I) { os << "[" << I.getA() << "," << I.getB() << "]";

return os;

}

#endif

7.2 The following program accomplishes the required task. It does its work by the following observation. Suppose the intervals are I1,I2,... ,In where Ij = [a j,b j] with a j < b j. Let α = maxj a j and β = minj b j. Interval Ik meets all intervals if and only if ak ≤ β and bk ≥ α.

#include "Interval.h" #include "uniform.h" using namespace std;

double find_max_A(const Interval* list, long ni) { double ans = list[0].getA();

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

if (ans < list[k].getA()) ans = list[k].getA();

}

return ans;

}

double find_min_B(const Interval* list, long ni) { double ans = list[0].getB();

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

if (ans > list[k].getB()) ans = list[k].getB();

}

return ans;

}

bool one_meets_all(const Interval* list, long ni) { double alpha = find_max_A(list,ni);

double beta = find_min_B(list,ni);

for (long k=0; k<ni; k++) {

if ((list[k].getA() <= beta) && (list[k].getB() >= alpha)) { return true;

}

}

return false;

}

int main() { seed(); long ni;

cout << "Enter number of intervals --> "; cin >> ni;

 

 

 

Answers

437

 

 

long nreps;

 

 

 

 

 

 

cout << "Enter number of repetitions --> ";

 

 

 

cin >> nreps;

 

 

 

 

 

 

Interval* list;

 

 

 

 

 

 

list = new Interval[ni];

 

 

 

 

 

long count = 0;

 

 

 

 

 

 

for (long j=0; j<nreps; j++) {

 

 

 

for (long k=0; k<ni; k++) {

 

 

 

list[k] = Interval(unif(), unif());

 

 

}

 

 

 

 

 

 

 

if (one_meets_all(list,ni)) count++;

 

 

}

 

 

 

 

 

 

 

cout << "Success rate: " << 100*double(count)/double(nreps)

 

 

 

<< "%" << endl;

 

 

 

 

 

delete[] list;

 

 

 

 

 

 

return 0;

 

 

 

 

 

}

 

 

 

 

 

 

 

Here is a sample run of the program.

 

 

 

 

 

 

 

 

 

Enter number of intervals --> 100

 

 

 

 

 

 

Enter number of repetitions --> 100000

 

 

 

Success rate: 66.659%

 

 

 

 

 

 

 

 

 

 

 

 

It appears that about 2

of the time there is an interval that meets all the others.

 

 

3

 

 

, the probability there is such an interval

 

One might

conjecture that as n

 

2

 

 

 

 

 

approaches

3 (and this is true).

On the other hand, it is not hard to show

that for n = 2, the probability that the two intervals intersect is exactly 23 . The surprise is that for any value of n 2, the probability there is an interval that intersects all the others is 23 . [Reference: J. Justicz, E. Scheinerman, and P. Winkler, Random intervals, American Mathematical Monthly 97 (December 1990) 881–889.]

7.3How can we sort the array and not modify it? Work with a copy. Here’s the code.

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

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

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

// make a copy of the array double* copy_array; copy_array = new double[nels];

438

C++ for Mathematicians

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

// sort the copy

sort(copy_array, copy_array+nels);

//extract the single middle element or the averages

//of two most central elements in the sorted list double ans;

if (nels%2 == 1) {

ans = copy_array[nels/2];

}

else {

ans = (copy_array[nels/2] + copy_array[(nels/2)+1]) / 2.;

}

//free the copy and return the result

delete[] copy_array; return ans;

}

7.4We can modify Program 7.3 to do the work. We need to generate all primitive Pythagorean triples containing a leg or a hypotenuse whose length is at most

100. To do this, we run the constructor PTriple(m,n) for all m,n 100. Note that if either m or n is greater than 100, then all three of 2mn, m2 n2, and m2 + n2 exceed 100 and may be ignored. Here’s a program.

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

/**

*Count the number of triples with a given leg and

*a given hypotenuse

*/

 

int main() {

 

PTriple* table;

// table to hold the triples

const int N = 100;

// maximum length we’re counting

int leg_count[N+1];

// tally leg lengths

int hyp_count[N+1];

// tally hypotenuse lengths

//Make sure counters are all zero for (int k=0; k<=N; k++) {

leg_count[k] = 0; hyp_count[k] = 0;

}

//Allocate space for the table table = new PTriple[2*N*N];

//Populate the table with all possible PTriples long idx = 0; // index into the table

for (long m=1; m<=N; m++) {

Answers

439

for (long n=1; n<=N; n++) { PTriple P = PTriple(m,n); if (P.getA() <= N) {

table[idx] = P; idx++;

}

}

}

//Sort the table sort(table, table+idx);

//Process unique elements in the tables leg_count[table[0].getA()]++; leg_count[table[0].getB()]++; hyp_count[table[0].getC()]++;

for (int k=1; k<idx; k++) {

if (table[k] != table[k-1]) { long a,b,c;

a = table[k].getA(); b = table[k].getB(); c = table[k].getC();

if (a<=N) leg_count[a]++; if (b<=N) leg_count[b]++; if (c<=N) hyp_count[c]++;

}

}

cout << "Length\tLeg\tHypotenuse" << endl; for (int k=0; k<=N; k++) {

cout << k << "\t" << leg_count[k] << "\t" << hyp_count[k] << endl;

}

// Release memory held by the table delete[] table;

return 0;

}

When the program is run, we see the following output.

 

 

 

 

 

Length

Leg

Hypotenuse

 

 

 

0

1

0

 

 

1

1

1

 

 

2

0

0

 

 

3

1

0

 

 

4

1

0

 

 

5

1

1

 

 

6

0

0

 

 

7

1

0

 

 

8

1

0

 

 

9

1

0

 

 

..................

 

 

90

0

0

 

 

91

2

0

 

 

 

 

 

 

 

 

440

 

 

C++ for Mathematicians

 

 

2

0

 

 

 

92

 

 

 

93

2

0

 

 

 

94

0

0

 

 

 

95

2

0

 

 

 

96

2

0

 

 

 

97

1

1

 

 

 

98

0

0

 

 

 

99

2

0

 

 

 

100

2

0

 

 

 

 

 

 

 

 

For an explanation of these results, see: Eric W. Weisstein. “Pythagorean Triple.” From MathWorld—A Wolfram Web Resource.

http://mathworld.wolfram.com/PythagoreanTriple.html

8.1Declare a set of sets of integers as set< set<long> > X; Incidentally, the following does not work: set<set<long>> X; because the >> can be con-

fused with the right-shift operator.

Here’s code to build the set

{

1,2,3

}

,

{

4,5

}

,

{

6

}

 

 

 

 

 

.

#include <set> using namespace std;

int main() { set<long> tmp;

set< set<long> > bigset;

tmp.insert(1);

tmp.insert(2);

tmp.insert(3);

bigset.insert(tmp);

tmp.clear();

tmp.insert(4);

tmp.insert(5);

bigset.insert(tmp);

tmp.clear();

tmp.insert(6);

bigset.insert(tmp);

return 0;

}

8.2 The procedure should be declared as

void print_set(const set<int>& A);

Here is the program.

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

Answers

441

void print_set(const set<int>& A) {

 

long n = A.size();

// number of el’ts in the set

 

long k;

// for counting the set

 

set<int>::iterator Ai;

// iterator into the set

 

cout << "{";

 

 

k = 0;

 

 

for (Ai = A.begin(); Ai != A.end(); ++Ai) {

 

cout << *Ai;

 

 

++k;

 

 

if (k < n) cout << ",";

// comma if not the last element

 

}

 

 

cout << "}";

 

 

}

 

 

8.3C++ set containers may only be used to house data types for which < and == are defined. The complex<double> type does not define <, so we cannot house these objects in a set.

However, C++ ordered pairs (pair<type1,type2>) can be held in a set provided < is defined for both type1 and type2.

Although we cannot put the value 2 + 3i into a set, we can put the ordered pair (2,3) into a set. We use code that looks like this.

#include <complex> #include <set> using namespace std;

int main() { complex<double> z(2.,3.);

set< pair<double,double> > A;

A.insert( make_pair(z.real(),z.imag()) ); return 0;

}

A more satisfactory solution is developed in Exercise 10.3.

8.4We must show that an equals the number of ordered factorizations of n.

Proof. The proof is by induction with the basis case a1 = 1 given. Suppose the result is true for all values less than n and we ask for the number of ordered factorizations of n. We condition on the first term in the factorization; say it’s d. The number of ordered factorizations of an whose first factor is d > 1 is exactly the number of ordered factorizations of n/d (the balance of the

factorization), and that equals an/d by induction. Summing over all d > 1 gives

an = an/d. d|n,d>1

However, this is just a rearranged version of the sum

ad

d|n,d<n

442

C++ for Mathematicians

and the result follows.

This sequence also arises in counting the number of perfect partitions of an integer. See the On-Line Encyclopedia of Integer Sequences, sequence A002033 and Eric W. Weisstein, “Perfect Partitions,” from Mathworld—A Wolfram Web Resource.

http://mathworld.wolfram.com/PerfectPartition.html

8.5Here is a file Partition.h that creates the class (with all methods and procedures written inline).

#ifndef PARTITION_H #define PARTITION_H

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

class Partition { private:

///A multiset to hold the parts multiset<int> parts;

///An integer to hold the sum int sum;

public:

///Default constructor: create null partition of 0 Partition() {

sum = 0; parts.clear();

}

///Add a new part to this partition

void add_part(int n) { if (n <= 0) {

cerr << "Cannot add nonpositive part to a partition" << endl;

return;

}

parts.insert(n); sum += n;

}

///What is the sum of the parts? int get_sum() const { return sum; }

///How many parts in this partition?

int nparts() const { return parts.size(); }

/// Get a vector containing the parts vector<int> get_parts() const {

vector<int> ans; ans.resize(nparts());

Answers

443

multiset<int>::iterator pi; int idx = 0;

for (pi=parts.begin(); pi!=parts.end(); pi++) { ans[idx] = *pi;

++idx;

}

return ans;

}

/// Compare two parts

bool operator<(const Partition& that) const {

//first compare the number partitioned if (sum < that.sum) return true;

if (sum > that.sum) return true;

//same sum, so compare number of parts if (nparts() < that.nparts()) return true;

if (nparts() > that.nparts()) return false;

//last resort, compare element by element vector<int> my_parts = get_parts(); vector<int> that_parts = that.get_parts();

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

if (my_parts[k] < that_parts[k]) return true; if (my_parts[k] > that_parts[k]) return false;

}

// Only way to reach here is if the partitions are equal return false;

}

};

inline ostream& operator<<(ostream& os, const Partition& P) { if (P.get_sum() == 0) {

os << 0; return os;

}

vector<int> list = P.get_parts(); int np = P.nparts();

os << P.get_sum() << " = "; for (int i=np-1; i>=0; i--) {

os << list[i];

if (i > 0) os << "+" ;

}

return os;

}

#endif

8.6Here is a program that uses a recursive approach conditioning on the largest part in the partition. The procedure make_partitions(n,mp) generates the set of all partitions of n whose largest part is at most mp.

#include "Partition.h"

444

C++ for Mathematicians

set<Partition> make_partitions(int n, int mp) { set<Partition> ans;

set<Partition> tmp;

if (mp>n) mp = n;

if (n==0) { ans.insert(Partition()); return ans;

}

for (int k=1; k<=n; k++) { tmp.clear();

tmp = make_partitions(n-k,k); set<Partition>::iterator sp;

for (sp = tmp.begin(); sp != tmp.end(); sp++) { Partition P = *sp;

P.add_part(k); ans.insert(P);

}

}

return ans;

}

set<Partition> make_partitions(int n) { return make_partitions(n,n);

}

int main() {

cout << "Enter n: "; int n;

cin >> n;

set<Partition> PS = make_partitions(n); set<Partition>::iterator pp;

for (pp = PS.begin(); pp != PS.end(); pp++) { cout << *pp << endl;

}

cout << PS.size() << " partitions of " << n << endl; return 0;

}

Here is a sample run of the program.

 

 

 

 

Enter n: 6

 

 

 

6

= 6

 

 

6

= 5+1

 

 

6

= 4+2

 

 

6

= 3+3

 

 

6

= 4+1+1

 

 

6

= 3+2+1

 

 

6

= 2+2+2

 

 

6

= 3+1+1+1

 

 

6

= 2+2+1+1

 

 

6

= 2+1+1+1+1

 

 

 

 

 

 

 

Answers

445

 

 

 

 

6 = 1+1+1+1+1+1

 

 

11 partitions of 6

 

 

 

 

 

8.7The solution is to use a static look-up table that takes pairs of long values as keys. To create such a table, be sure to #include <map> and give the following declaration.

static map< pair<long,long> , long > table;

Here is a program with a binomial procedure and a main to check that it works.

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

long binomial(long n, long k) {

static map< pair<long,long> , long > table;

if (k>n) return 0; if (k==0) return 1; if (k==n) return 1;

pair<long, long> args = make_pair(n,k);

if (table.count(args) > 0) { return table[args];

}

table[args] = binomial(n-1,k-1) + binomial(n-1,k);

return table[args];

}

int main() {

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

cout << binomial(n,k) << "\t";

}

cout << endl;

}

return 0;

}

The code gives Pascal’s triangle as output.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

0

0

0

0

0

0

0

0

0

 

1

1

0

0

0

0

0

0

0

0

 

1

2

1

0

0

0

0

0

0

0

 

1

3

3

1

0

0

0

0

0

0

 

1

4

6

4

1

0

0

0

0

0

 

1

5

10

10

5

1

0

0

0

0

 

1

6

15

20

15

6

1

0

0

0

 

1

7

21

35

35

21

7

1

0

0

 

1

8

28

56

70

56

28

8

1

0

 

1

9

36

84

126

126

84

36

9

1