C++ for Mathematicians An Introduction for Students and Professionals phần 5 pdf

52 533 1
C++ for Mathematicians An Introduction for Students and Professionals phần 5 pdf

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

The Projective Plane 185 30 int main() { 31 Base B(1,2); 32 Child C(3,4); 33 GrandChild D(5,6,7); 34 35 B.print(); cout << endl; 36 // cout << B.sum() << endl; // Illegal, sum is protected 37 38 C.print(); cout << " > "; 39 C.increase_b(); 40 C.print(); cout << endl; 41 42 D.print(); cout << " > "; 43 D.increase_b(); 44 D.print(); cout << endl; 45 46 return 0; 47 } In this program we define three classes: Base, Child,andGrandChild; each is used to derive the next. The Base class has two data members: a private integer a and a protected integer b. The class also includes a protected method named sum, a public constructor, and a public method named print. Class Child has no additional data members. It has a public method increase_b that increases the data member b by one. Note that it would not be possible for Child to have a similar method for increasing a. The constructor for Child passes its arguments on to its parent, Base, but then takes no further action (hence the curly braces on line 17 do not enclose any statements). The print method for Child uses Base’s print method and sum method. Class GrandChild adds an extra p rivate data element, c. The constructor for GrandChild passes its first two arguments to its parent’s constructor and then uses the third argument to set the value of c. The print method for GrandChild uses its grandparent’s print method. Al- though Base::print() invokes the sum method, the GrandChild methods cannot directly call sum because it is protected in Base, hence implicitly private in Child, and hence inaccessible in GrandChild. A main to illustrate all these ideas begins on line 30 . The output of the program follows. ✞ ☎ (1,2) (3,4)=7 > (3,5)=8 (5,6)/7 > (5,7)/7 ✝ ✆ 186 C++ for Mathematicians 10.5 Class and file organization for PPoint and PLine Because of the duality between points and lines in the projective plane, most of the data and code we use to represent these concepts in C++ are the same. If at all possible, we should avoid writing the same code twice for two reasons. First, the initial work in creating the programs is doubled. More important, maintaining the code is also made more difficult; if a change is required to the code, we need to remember to make that change twice. When we are fussing with our programs and making a number of minor modifications, it is easy to forget to update both versions. To illustrate this, we deliberately include a subtle flaw in the first version of the program; we then repair the problem once (not twice). To this end, we define three classes: a parent class named PObject that contains as much of the code as possible and two derived classes, PPoint and PLine,that include code particular to each. These classes are defined in two files each: a header .h file and a code .cc file. Figure 10.3 illustrates the organ ization. PObject PObject.h PObject.cc PPoint PPoint.h PPoint.cc PLine PLine.h PLine.cc Figure 10.3: Hierarchy of the PObject classes. The header file PObject.h is used to declare the PObject class. Both PPoint.h and PLine.h require the directive #include "PObject.h". Programs that use projective geometry need all three. Rather than expecting the user to type multiple #include directives, we create a convenient header file that includes everything we need. We call this header file Projective.h;hereitis. Program 10.3: Header file for all pro jective geometry classes, Projective.h. 1 #ifndef PROJECTIVE_H 2 #define PROJECTIVE_H 3 #include "PPoint.h" 4 #include "PLine.h" 5 #endif The Projective Plane 187 Notice that we do not require #include "PObject.h" because that file is al- ready #includedbyPPoint.h and PLine.h. All these files have the usual struc- ture to prevent multiple inclusion. All told, we have seven files that implement points and lines in the projective plane. PObject.h The header file for the PObject class. PObject.cc The C++ code for the PObject class. PPoint.h The header file for the PPoint class. PPoint.cc The C++ code for the PPoint class. PLine.h The header file for the PLine class. PLine.cc The C++ code for the PLine class. Projective.h The only header file subsequent programs need to include to use projective points and lines. The decision to write the program in seven different files is based on the principle of breaking a problem down to manageable sizes and working on each piece sep- arately. We could have packed all this work into two larger files (one .h and one .cc). 10.6 The parent class PObject Data and functionality common to PPoint and PLine are implemented in the class PObject. Using the ideas presented in Section 10.2, we map out the class PObject. Data A point or line in RP 2 is represented by homogeneous coordinates: (x,y,z) or [x,y,z]. To hold these coordinates, we declare three private double variables, x, y,andz. Let us write x,y,z for the homogeneous coordinates of a generic projective object (either a point of a line). (See Program 10.4, line 9.) Because 2,1, −5 and −4,−2, 10 name the same object, it is useful to choose a canonical triple. One idea is to make sure that x,y,z is a unit vector (but then we have a sign ambiguity). The representation we use is to make the last nonzero coordinate of the triple equal to 1. The motivation for this choice is that a point in the Euclidean plane at coordinates (x,y) corresponds to the point (x, y,1) in the projective plane. If later we are unhappy with this decision, we can choose another manner to store the homogeneous coordinates. Because the coordinates are private members of PObject, we would only n eed to update the code for PObject. 188 C++ for Mathematicians We provide public methods getX(), getY(),andgetZ() to inspect (but not modify) the values held in x, y,andz, respectively. (See Program 10.4 lines 33–35.) We reserve 0, 0,0to stand for an invalid projective object. We provide a pub- lic method is_invalid() to check if an object is invalid. ( See Program 10.4 lines 37–39.) Constructors It is natural to define a constructor for a PObject with three parame- ters that set the homogeneous coordinates of the object. The user might invoke the constructor like this: PObject P(2., 3., 5.); Rather than holding this point as 2,3,5, we use the canonical representation 0.4,0.6, 1. (See Program 10.4 lines 25–30.) To facilitate the conversion of user-supplied coordinates to canonical coordi- nates, we create a private helper procedure scale. (See Program 10.4 line 10 and Program 10.5 lines 4–19.) All C++ classes ought to define a zero-argument constructor that creates a default object of that class. In this case, a sensible choice is the object 0,0,1. This corresponds to the origin (as a point) or the line at infinity (as a line) . (See Program 10.4 lines 21–24.) Relations We want to be able to co mpare projective points (and lines) f or equality, inequality, and < (for sorting). To this end, we might be tempted to define operator== in the public portion of PObject like this: public: bool operator==(const PObject& that) const { return ( (x==that.x) && (y==that.y) && (z==that.z) ); } Although this would give the desired result when comparing points to points or lines to lines, it would also provide the unfortunate ability to compare points to lines. Were we to use the above method for testing equality, then we might run into the following situation. PPoint P(2., 3., 5.); PLine L(2., 3., 5.); if (P == L) { cout << "They are equal." << endl; } When this code runs, the message They are equal wou ld be written on the computer’s screen. There are at least two problems with this approach. First, the point (2,3,5) and the line [2 , 3,5] are not equal even though they have the same homogeneous The Projective Plane 189 coordinates. Second, lines and points should not even be comparable by ==. Code that compares a point to a line is alm ost cer tainly a bug; the best thing in this situation is for the compiler to flag such an expression as an error. We therefore take a different approach to equality testin g. We want the fun- damental code that checks for equality to reside inside the PObject class be- cause that code is common to both PPoint and PLine. We d o this by declar- ing a protected method called equals inside PObject (see Program 10.4 line 14): protected: bool equals(const PObject& that) const; In the file PObject.cc we give the code for this method (see Program 10.5 lines 34–36): bool PObject::equals (const PObject& that) const { return ( (x==that.x) && (y==that.y) && (z==that.z) ); } Then, in the class definitions for PPoint and PLine we give the necessary operator definitions. For example, in PLine we have this: public: bool operator==(const PLine& that) const { return equals(that); } bool operator!=(const PLine& that) const { return !equals(that); } In this way, if (indeed, when) we decide to change the manner in which we test for equality, we only need to update PObject’s protected equals method. Similarly, rather than defining a < operator for PObject,wedefine a protected less method. The children can access less to define their individual, public operator< methods. (See Program 10.4 line 15 and Program 10.5 lines 38– 45.) Meet/Join Operation Given two PPoints P and Q,wewantP+Q to return the line through those points. Dually, given PLines L and M,wewantL * M to return the point of intersection of these lines. In both cases, the calculations are the same: given the triples x 1 ,y 1 ,z 1  and x 2 ,y 2 ,z 2  we need to find a new triple x 3 ,y 3 ,z 3  that is orthogonal to the first two. To do this, we calculate the cross product: x 3 ,y 3 ,z 3  = x 1 ,y 1 ,z 1 ×x 2 ,y 2 ,z 2 . Therefore, in PObject we declare a protected method called op that is invoked by operator+ in PPoint and operator * in PLine. (See Program 10.4 line 18 and Program 10.5 lines 138–148.) 190 C++ for Mathematicians Incidence Given a point and a line, we want to be able to determine if the point lies on the line. If the coordinates for these are (x,y,z) and [a,b,c], r espectively, then we simply need to test if ax + by + cz = 0. It does not make sense to ask if one line is incident with another, so we do not make an “is incident with” method publicly available in PObject.Rather,we make a protected incident method (that calls, in turn, a private dot method for calculating dot product). (See Program 10.4 lines 11,16 and Program 10.5 lines 21–23,47–49.) Then, PPoint and PLine can declare their own public methods that access incident. Details on this later. Collinearity/Co ncurrence Are three given points collinear? Are three given lines concurrent? If the three objects have coordinates x 1 ,y 1 ,z 1 , x 2 ,y 2 ,z 2 ,and x 3 ,y 3 ,z 3 , then the answer is yes if and only if the vectors are linearly depen- dent. We check this by calculating det ⎡ ⎣ x 1 y 1 z 1 x 2 y 2 z 2 x 3 y 3 z 3 ⎤ ⎦ and seeing if we get zero. In PObject.h we declare a procedure dependent that takes three PObject arguments and returns true or false. We then use dependent to implement procedures collinear and concurrent for the classes PPoint and PLine (respectively). (See Program 10.4 line 44 and Program 10.5 lines 60–77.) Random points/lines There is no way to generate a point uniformly at random in the Euclid ean plane, but there is a sensible way in which we can do this for the projective plane. Recall that points in RP 2 correspond to lines through the origin in R 3 . Thus, to select a point at random in RP 2 we generate a point uniformly at random on the unit ball centered at the origin. An efficient way to perform this latter task is to select a vector v uniformly in [−1,1] 3 .Ifv> 1, then we reject v and try again. The method for generating a random line is precisely the same. We therefore include a public method randomize that resets the coordinates of the PObject by the algorithm we just described. (See Program 10.4 line 31 and Program 10.5 lines 25–32.) To choose a random line through a point is similar. Suppose the point is (x,y,z). The line [a,b,c] should be chosen so that [a, b,c] is orthogonal to (x,y,z).Todothis,wefind an orthonormal basis for (x,y,z) ⊥ that we denote {(a 1 ,b 1 ,c 1 ),(a 2 ,b 2 ,c 2 )}. We then choose t uniformly at random in [0,2 π ]. The random line [a,b,c] is given by a = a 1 cost + a 2 sintb= b 1 cost + b 2 sintc= c 1 cost + c 2 sint. The Projective Plane 191 Rather than generate t and then compute two trigonometric functions, we can obtain the pair (cost,sint) by choosing a point uniformly at random in the unit disk (in R 2 ) and then scaling. The technique for selecting a random point on a g iven line is exactly the same. Thus, we define a protected rand_perp method in PObject (Program 10.4 line 17 and Program 10.5 lines 79–136). The rand_perp method is used by rand_point in PLine and rand_line in PPoint. Output Finally, PObject.h declares a procedure for writing to an output stream. The format is <x,y,z>.Theostream operator<< defined in PObject is overridden by like-named p rocedures in PPoint and PLine.(SeePro- gram 10.4 line 42 and Program 10.5 lines 51–58.) With these explanations in place, we now g ive the header and code files for the class PObject. Program 10.4: Header file for the PObject class (version 1). 1 #ifndef POBJECT_H 2 #define POBJECT_H 3 4 #include <iostream> 5 using namespace std; 6 7 class PObject { 8 private: 9 double x,y,z; 10 void scale(); 11 double dot(const PObject& that) const; 12 13 protected: 14 bool equals(const PObject& that) const; 15 bool less(const PObject& that) const; 16 bool incident(const PObject& that) const; 17 PObject rand_perp() const; 18 PObject op(const PObject& that) const; 19 20 public: 21 PObject() { 22 x=y=0.; 23 z=1.; 24 } 25 PObject(double a, double b, double c) { 26 x=a; 27 y=b; 28 z=c; 29 scale(); 30 } 31 void randomize(); 32 33 double getX() const { return x; } 192 C++ for Mathematicians 34 double getY() const { return y; } 35 double getZ() const { return z; } 36 37 bool is_invalid() const { 38 return (x==0.) && (y==0.) && (z==0.); 39 } 40 }; 41 42 ostream& operator<<(ostream& os, const PObject& A); 43 44 bool dependent(const PObject& A, const PObject& B, const PObject& C); 45 46 #endif Program 10.5: Program file for the PObject class (version 1). 1 #include "PObject.h" 2 #include "uniform.h" 3 4 void PObject::scale() { 5 if (z != 0.) { 6 x/=z; 7 y/=z; 8 z=1.; 9 return; 10 } 11 if (y != 0.) { 12 x/=y; 13 y=1.; 14 return; 15 } 16 if (x != 0) { 17 x=1.; 18 } 19 } 20 21 double PObject::dot(const PObject& that) const { 22 return x * that.x + y * that.y + z * that.z; 23 } 24 25 void PObject::randomize() { 26 do { 27 x = unif(-1.,1.); 28 y = unif(-1.,1.); 29 z = unif(-1.,1.); 30 } while (x * x+y * y+z * z > 1.); 31 scale(); 32 } 33 34 bool PObject::equals(const PObject& that) const { 35 return ( (x==that.x) && (y==that.y) && (z==that.z) ); 36 } 37 38 bool PObject::less(const PObject& that) const { 39 if (x < that.x) return true; The Projective Plane 193 40 if (x > that.x) return false; 41 if (y < that.y) return true; 42 if (y > that.y) return false; 43 if (z < that.z) return true; 44 return false; 45 } 46 47 bool PObject::incident(const PObject& that) const { 48 return dot(that)==0.; 49 } 50 51 ostream& operator<<(ostream& os, const PObject& A) { 52 os << "<" 53 << A.getX() << "," 54 << A.getY() << "," 55 << A.getZ() 56 << ">"; 57 return os; 58 } 59 60 bool dependent(const PObject& A, const PObject& B, const PObject& C){ 61 double a1 = A.getX(); 62 double a2 = A.getY(); 63 double a3 = A.getZ(); 64 65 double b1 = B.getX(); 66 double b2 = B.getY(); 67 double b3 = B.getZ(); 68 69 double c1 = C.getX(); 70 double c2 = C.getY(); 71 double c3 = C.getZ(); 72 73 double det = a1 * b2 * c3 + a2 * b3 * c1+a3 * b1 * c2 74 -a3 * b2 * c1 - a1 * b3 * c2-a2 * b1 * c3; 75 76 return det == 0.; 77 } 78 79 PObject PObject::rand_perp() const { 80 if (is_invalid()) return PObject(0,0,0); 81 82 double x1,y1,z1; // One vector orthogonal to (x,y,z) 83 double x2,y2,z2; // Another orthogonal to (x,y,z) and (x1,y1,z1) 84 85 if (z == 0.) { // If z==0, take (0,0,1) for (x1,y1,y2) 86 x1 = 0; 87 y1 = 0; 88 z1 = 1; 89 } 90 else { 91 if (y == 0.) { // z != 0 and y == 0, use (0,1,0) 92 x1=0; 93 y1=1; 94 z1=1; 95 } 194 C++ for Mathematicians 96 else { // y and z both nonzero, use (0,-z,y) 97 x1=0; 98 y1=-z; 99 z1=y; 100 } 101 } 102 103 // normalize (x1,y1,z1) 104 double r1 = sqrt(x1 * x1 + y1 * y1 + z1 * z1); 105 x1 /= r1; 106 y1 /= r1; 107 z1 /= r1; 108 109 // (get x2,y2,z2) by cross product with (x,y,z) and (x1,y1,z1) 110 x2 = -(y1 * z)+y * z1; 111 y2 = x1 * z-x * z1; 112 z2 = -(x1 * y)+x * y1; 113 114 // normalize (x2,y2,z2) 115 double r2 = sqrt(x2 * x2 + y2 * y2 + z2 * z2); 116 x2 /= r2; 117 y2 /= r2; 118 z2 /= r2; 119 120 // get a point uniformly on the unit circle 121 double a,b,r; 122 do { 123 a = unif(-1.,1.); 124 b = unif(-1.,1.); 125 r=a * a+b * b; 126 } while (r > 1.); 127 r = sqrt(r); 128 a/=r; 129 b/=r; 130 131 double xx = x1 * a+x2 * b; 132 double yy = y1 * a+y2 * b; 133 double zz = z1 * a+z2 * b; 134 135 return PObject(xx,yy,zz); 136 } 137 138 139 PObject PObject::op(const PObject& that) const { 140 141 if (equals(that)) return PObject(0,0,0); 142 143 double c1 = y * that.z - z * that.y; 144 double c2 = z * that.x - x * that.z; 145 double c3 = x * that.y - y * that.x; 146 147 return PObject(c1,c2,c3); 148 } [...]... collinear, as guaranteed by Pappus’s theorem" . > (3 ,5) =8 (5, 6)/7 > (5, 7)/7 ✝ ✆ 186 C++ for Mathematicians 10 .5 Class and file organization for PPoint and PLine Because of the duality between points and lines in the projective plane, most. PPoint and PLine.(SeePro- gram 10.4 line 42 and Program 10 .5 lines 51 58 .) With these explanations in place, we now g ive the header and code files for the class PObject. Program 10.4: Header file for. output: ✞ ☎ The random point P is (-1.324 45, 0 .59 1 751 ,1) Two lines through P are L = [6 .51 303,12.88 75, 1] and M = [0.871229,0.260071,1] IsPonL?0 Does M have P? 0 The point of intersection of L and M is

Ngày đăng: 12/08/2014, 12:20

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan