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

C++ For Mathematicians (2006) [eng]

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

226

C++ for Mathematicians

Program 11.2: Program file for Permutation class.

1 #include "Permutation.h"

2#include "uniform.h"

3

4 Permutation::Permutation() {

5 data = new long[2];

6n = 1;

7data[1] = 1;

8}

9

10Permutation::Permutation(long nels) {

11if (nels <= 0) {

12nels = 1;

13}

14data = new long[nels+1];

15n = nels;

16for (long k=1; k<=n; k++) {

17data[k] = k;

18}

19}

20

21Permutation::Permutation(long nels, long* array) {

22if (nels <= 0) {

23nels = 1;

24data = new long[2];

25data[1] = 1;

26return;

27}

28n = nels;

29data = new long[n+1];

30for (long k=1; k<=n; k++) data[k] = array[k];

31if (!check()) reset();

32}

33

34Permutation::Permutation(const Permutation& that) {

35n = that.n;

36data = new long[n+1];

37for (long k=1; k<=n; k++) data[k] = that.data[k];

38}

39

40Permutation::˜Permutation() {

41delete[] data;

42}

43

44void Permutation::reset() {

45for (long k=1; k<=n; k++) data[k] = k;

46}

47

48void Permutation::swap(long i, long j) {

49if ( (i<1) || (i>n) || (j<1) || (j>n) || (i==j) ) return;

50long a = data[i];

51long b = data[j];

52data[i] = b;

53data[j] = a;

54}

Permutations

227

55

56void Permutation::randomize() {

57for (long k=1; k<n; k++) {

58long j = unif(n-k+1)-1+k;

59long tmp = data[j];

60data[j] = data[k];

61data[k] = tmp;

62}

63}

64

65bool Permutation::check() const {

66long* temp;

67

68temp = new long[n+1];

69for (long k=1; k<=n; k++) {

70if ( (data[k] < 1) || (data[k] > n)) {

71delete[] temp;

72return false;

73}

74temp[k] = data[k];

75}

76sort(temp+1, temp+n+1);

77for (long k=1; k<=n; k++) {

78if (temp[k] != k) {

79delete[] temp;

80return false;

81}

82}

83delete[] temp;

84return true;

85}

86

87long Permutation::of(long k) const {

88if ( (k<1) || (k>n) ) return k;

89return data[k];

90}

91

92Permutation Permutation::operator=(const Permutation& that) {

93delete[] data;

94n = that.n;

95data = new long[n+1];

96for (long k=1; k<=n; k++) data[k] = that.data[k];

97return *this;

98}

99

100Permutation Permutation::operator*(const Permutation& that) const {

101long nmax = (n > that.n) ? n : that.n;

102long* tmp = new long[nmax+1];

103

104for (long k=1; k<=n; k++) {

105tmp[k] = of(that(k));

106}

107

108Permutation ans(nmax,tmp);

109delete[] tmp;

110return ans;

228

C++ for Mathematicians

111 }

112

113Permutation Permutation::operator*=(const Permutation& that) {

114(*this) = (*this) * that;

115return *this;

116}

117

118Permutation Permutation::inverse() const {

119Permutation ans(n);

120for (long k=1; k<=n; k++) ans.data[data[k]] = k;

121return ans;

122}

123

124bool Permutation::operator==(const Permutation& that) const {

125if (n != that.n) return false;

126

127for (long k=1; k<=n; k++) {

128if (data[k] != that.data[k]) return false;

129}

130return true;

131}

132

133bool Permutation::operator!=(const Permutation& that) const {

134return !( (*this)==that );

135}

136

137bool Permutation::operator<(const Permutation& that) const {

138if (n < that.n) return true;

139if (n > that.n) return false;

140

141for (long k=1; k<=n; k++) {

142if (data[k] < that.data[k]) return true;

143if (data[k] > that.data[k]) return false;

144}

145return false;

146}

147

148bool Permutation::isIdentity() const {

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

150if (data[k] != k) return false;

151}

152return true;

153}

154

155ostream& operator<<(ostream& os, const Permutation& P) {

156long n = P.getN();

157bool* done = new bool[n+1];

158for (long k=1; k<=n; k++) done[k] = false;

159

160for (long k=1; k<=n; k++) {

161if (!done[k]) {

162os << "(" << k;

163done[k] = true;

164long j = P(k);

165while (j!=k) {

166os << "," << j;

Permutations

229

167done[j] = true;

168j = P(j);

169}

170os << ")";

171}

172}

173delete[] done;

174return os;

175}

11.3Finding monotone subsequences

We now return to Ulam’s problem: what is the expected length of a longest increasing subsequence of a random permutation? The Permutation class includes a randomize method, so we are able to generate permutations uniformly at random in Sn. Given a permutation, how do we find the length of a longest increasing (or decreasing) subsequence?

The idea for the algorithm comes from the proof of the Erdos˝–Szekeres Theorem (Theorem 11.1). Given a permutation π Sn, we define the values ui (respectively, di) to be the length of a longest increasing (respectively, decreasing) subsequence of π starting at position i. To find the values ui and di we work from right to left; that is, we start with i = n and work our way down.

Clearly un = dn = 1 because the longest increasing (decreasing) sequence that starts from the last position has length exactly one.

Now consider un−1 and dn−1. If π(n − 1) < π(n), then the longest increasing subsequence starting at position n −1 has length 2 and the longest decreasing subsequence has length 1. On the other hand, if π(n −1) > π(n), then the longest increasing subsequence starting at position n − 1 has length 1 and the longest decreasing subsequence has length 2. In other words,

(un−1,dn−1) =

((1,2)

if π(n

1) > π(n).

and

 

(2,1)

if π(n

1) < π(n),

Suppose we have established the values ui and di for i = k + 1 through i = n. To find uk, we compare π(k) sequentially with π(k + 1), π(k + 2), and so on, up to π(n). Among all indices j > k for which π(k) < π( j), find the one for which uj is largest. We then set uk = uj + 1. If there are no indices j with π(k) < π( j), then we set uk = 1.

The procedure for finding dk is analogous. Among all indices j > k for which π(k) > π( j), we select the j for which dj is largest and set dk = dj + 1. If no such index j exists, we set dk = 1.

230

C++ for Mathematicians

Let’s consider an example. For the permutation π = [1,4,7,2,5,3,6], suppose we have calculated ui and di for all i ≥ 3. We now want to find u2 and d2. This is what we know so far.

Index i

1

2

3

4

5

6

7

π(i)

1

4

7

2

5

3

6

ui

?

?

1

3

2

2

1

di

?

?

3

1

2

1

1

To find u2, note that for indices j = 3, 5, and 7 we have π(2) < π( j). Note that u3 = 1, u5 = 2, and u7 = 1, so u5 is largest. We therefore set u2 = u5 + 1 = 3. Indeed, the longest increasing subsequence starting at position 2 is [4,5,6].

Finding d2 is analogous. For indices j = 4 and 6 we have π(2) > π( j). Note that d4 = 1 and d6 = 1, so we set d2 = 2.

We create a procedure named monotone that takes a Permutation as its argument and returns a pair of long integers giving the lengths of the longest increasing and decreasing subsequences in the Permutation. Here is the header file, which we call monotone.h.

Program 11.3: Header file monotone.h.

1 #ifndef MONOTONE_H

2#define MONOTONE_H

3

4 #include "Permutation.h"

5#include <utility>

6

7pair<long,long> monotone(const Permutation& P);

8

9#endif

Note that we have #include <utility> because the monotone procedure returns a pair. The utility header file is needed to define the pair class.

The code for this procedure is housed in a file named monotone.cc that we present next.

Program 11.4: A program to find the length of the longest monotone subsequences of a permutation.

1#include "monotone.h"

2

3pair<long,long> monotone(const Permutation& P) {

4long* up;

5long* dn;

6long n = P.getN();

7

8 up = new long[n+1];

9dn = new long[n+1];

10

11for (long k=1; k<=n; k++) {

12up[k] = dn[k] = 1;

Permutations

231

13

}

14

 

15for (long k=n-1; k>=1; k--) {

16for (long j=k+1; j<=n; j++) {

17if (P(k) > P(j)) {

18if (dn[k] <= dn[j]) {

19dn[k] = dn[j]+1;

20}

21}

22else {

23if (up[k] <= up[j]) {

24up[k] = up[j]+1;

25}

26}

27}

28}

29

30long up_max = 1;

31long dn_max = 1;

32for (long k=1; k<=n; k++) {

33if (up_max < up[k]) up_max = up[k];

34if (dn_max < dn[k]) dn_max = dn[k];

35}

36

37delete[] up;

38delete[] dn;

39

40return make_pair(up_max,dn_max);

41}

The arrays up and dn are used to hold the sequences ui and di.

Finally, we need a main to generate random permutations repeatedly and calculate the average lengths of longest increasing and decreasing subsequences. Here is such a program.

Program 11.5: A program to illustrate Ulam’s problem.

1 #include "Permutation.h"

2 #include "uniform.h"

3 #include "monotone.h"

4 #include <iostream>

5using namespace std;

6

7 int main() {

8long n;

9long reps;

10

seed();

11

 

12cout << "Enter n (size of permutation) --> ";

13cin >> n;

14

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

15

cin >> reps;

16

 

17

Permutation P(n);

18

 

232

C++ for Mathematicians

19long sum_up = 0;

20long sum_dn = 0;

21

22for (long k=0; k<reps; k++) {

23P.randomize();

24pair<long,long> ans;

25ans = monotone(P);

26sum_up += ans.first;

27sum_dn += ans.second;

28}

29

30cout << "Average length of longest increasing subsequence is "

31<< double(sum_up)/double(reps) << endl;

32

33cout << "Average length of longest decreasing subsequence is "

34<< double(sum_dn)/double(reps) << endl;

35

36return 0;

37}

 

 

Here is the result of running this program for permutations in Sn for n equal to

 

 

 

 

10,000 for one hundred iterations.

 

 

 

 

 

Enter n

(size of permutation) -->

10000

 

 

 

 

 

 

Enter number of repetitions -->

100

 

 

 

 

Average

length of longest increasing subsequence is 192.31

 

 

 

 

Average

length of longest decreasing subsequence is 192.94

 

 

 

 

 

 

 

For this case, we observe E(Ln)/n ≈ 1.9. In fact,

lim E(Ln) = 2.

n→∞ n

11.4Exercises

11.1Devise a procedure or method to find the order of a permutation. (For a permutation π, the order of π is the least positive integer k so that πk is the identity.)

11.2Create a class name Counted that can keep track of the number of objects of type Counted which exist at any point in the program. That is, the Counted class should include a static method named count that returns the number of Counted objects currently in memory.

For example, consider the following main().

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

int main() {

Permutations

Counted X, Y; Counted* array;

array = new Counted[20];

cout << "There are " << Counted::count()

<< " Counted objects in memory" << endl;

delete[] array;

cout << "And now there are " << Counted::count() << " Counted objects in memory" << endl;

return 0;

}

This should produce the following output.

There are 22 Counted objects in memory

And now there are 2 Counted objects in memory

233

(It’s hard to imagine a mathematical reason we might want to keep track of how many objects of a given sort are being held in memory, but this is a useful debugging trick to see if there is a memory leak.)

11.3Redo Exercise 8.5 (page 152) without using the container classes of the Standard Template Library. That is, the parts of the partition should be held in a conventional C++ array and maintained in sorted order. This change requires you to create a copy constructor, an assignment operator, and a destructor.

Replace the get_parts method by an operator[]; if P is a Partition, then the expression P[k] should give the kth part of the partition.

11.4Let n be a positive integer. Suppose that n points are placed in the unit cube [0,1]3. Write a procedure to find the size s of a largest subset of these n points

such that their coordinates satisfy

x1 ≤ x2 ≤ x3 ≤ ··· ≤ xs y1 ≤ y2 ≤ y3 ≤ ··· ≤ ys z1 ≤ z2 ≤ z3 ≤ ··· ≤ zs

Suppose the points are placed in the unit cube independently and uniformly at random. Use your procedure to conjecture the expected size of the largest such set.

11.5Create a class SmartArray that behaves as a C++ array on long values, but that allows any integer subscript. That is, a SmartArray is declared like this:

SmartArray X(100);

This creates an object X that has the same behavior as if it were declared long X[100]; but allows indexing beyond the range 0 to 99. An index of

234

C++ for Mathematicians

k outside this range is replaced by k mod 100. In this way, X[-1] refers to the last element of the array (in this case X[99]).

Hint: The hard part for this problem is enabling the expression X[k] to appear on the left side of an assignment. Suppose you declare the indexing operator like this:

long operator[](long k);

Then you cannot have an expression such as X[2]=4;. The trick is to declare the operator like this:

long& operator[](long k);

11.6Create a class to represent linear fractional transformations. These are functions of the form

f (z) = az + b cz + d

where z,a,b,c,d C.

Be sure to include an operator() for evaluating a linear fractional transformation at a given complex number and an operator* for the composition of two transformations.

11.7Create a Path class that represents a polygonal path in the plane (i.e., an ordered sequence of Points in R2). Include the following methods:

A default constructor that creates an empty path.

A one-argument constructor that creates a path containing a single point.

An operator+ that concatenates two paths, or concatenates a path and a point. (Be sure to take care of both Path+Point and Point+Path.)

An operator[] to get the kth point on the path.

Chapter 12

Polynomials

In this chapter we create a C++ class to represent polynomials. The coefficients of these polynomials can be from any field K such as the real numbers, the complex numbers, or Zp. One way to do this is to create several different polynomial classes depending on the coefficient field. A better solution is to learn how to use C++ templates.

12.1Procedure templates

Imagine that we often need to find the largest of three quantities in our programming. We create a procedure named max_of_three like this:

long max_of_three(long a, long b, long c) { if (a<b) {

if (b<c) return c; return b;

}

if (b<c) return c; return b;

}

Then, the expression max_of_three(3,6,-2) evaluates to 6.

If we also want to find the maximum of three real values, we create another version of max_of_three:

double max_of_three(double a, double b, double c) { if (a<b) {

if (b<c) return c; return b;

}

if (b<c) return c; return b;

}

Pythagorean triples (see Chapter 7) can also be compared by <, so if we want to compare PTriple objects, we create yet another version of max_of_three:

PTriple max_of_three(PTriple a, PTriple b, PTriple c) { if (a<b) {

if (b<c) return c;

235