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

C++ For Mathematicians (2006) [eng]

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

Part III

Topics

Chapter 13

Using Other Packages

Good news!

The first good news is that we have covered nearly all of the C++ concepts you need to know for mathematical work. All that remains is a more extensive discussion on getting information in and out of your programs (covered next in Chapter 14).

The second good news is we are ready to stand on the shoulders of giants. If you are reading this book, chances are you are not an expert in computer programming. So, the next best thing is to have an expert assistant to create C++ classes for you. And you do! Thanks to the ubiquity of C++, there are classes available for many types of work including number theory, algebraic geometry, optimization, quaternions, combinatorics, cup products for finite groups, and more. Many of these packages are available for free (for noncommercial use) over the Web.

In this chapter we introduce a few of these packages.

13.1Arbitrary precision arithmetic: The GMP package

The C++ long type can accommodate integer values in a finite range (see Section 2.1). For work in number theory or cryptography, one needs to handle integers with hundreds or thousands of digits. Or perhaps we want to work with rational values, but double variables are unacceptable because they do not hold exact values. The solution to this problem is to create C++ classes for handling arbitrarily large integers and exact rational numbers.

The creation of such classes takes a lot of work, and making the algorithms efficient takes a great deal of skill. Fortunately, programmers with a great deal of skill have done the hard work of creating the GNU Multiple Precision Library (called GMP for short). (We describe version 4.1.4.)

The GMP library is available on the Web at http://www.swox.com/gmp/, but it may already be available on your computer (it is often included on Linux systems). The underlying GMP library is created for programming in C, but it includes good support for C++ programming. If GMP has not already been installed on your computer, you can download it from the Web and follow the installation instructions (see the file install or the manual in PDF format). If you run into trouble, find a friendly computer scientist for some assistance.

269

270

C++ for Mathematicians

The GMP package provides four important C++ classes:

mpz_class for arbitrary precision integers,

mpq_class for exact rational numbers,

mpf_class for floating point numbers, and

gmp_randclass for generating large random numbers.

To use these classes, your program needs to include a header file:

#include <gmpxx.h>

Variables are declared in the usual manner. For example, to declare x to be an arbitrary precision integer, use this:

mpz_class x;

This initializes x with the value 0. To give it a large value, the following does not work.

x = 3098472938750987439857234543534232626245985;

// this fails

The problem is that C++ is not able to deal with that large value as one of its basic types. Instead, you can type this:

x = "3098472938750987439857234543534232626245985"; // this works

The mpz_class type can convert character arrays (containing digits). The mpz_class can be initialized using other bases. For example,

mpz_class x("12321423112312321312314001200001213", 5);

initializes x with a base-5 value.

The usual arithmetic and comparison operators work just as expected. Multiplication of extremely large integers is accomplished using sophisticated efficient algorithms.

The rational type, mpq_class, is constructed either from two integer arguments

mpq_class a(n_1,n_2);

or else a character array, such as this:

mpq_class b("53490875234097/1134381");

Rationals can also be constructed from double values. Indeed, the GMP types can be converted to and constructed from nearly any numeric type.

Rational mpq_class objects have a canonicalize() method. The purpose of this method is to clear any common factors between numerator and denominator. It’s a good idea to invoke this method after creating an mpq_class value.

Using Other Packages

271

The C++ features of the GMP package are a supplement to the C base that forms the bulk of GMP. Some GMP procedures require some fancy footwork to be used in C++. For example, to find the greatest common divisor of two mpz_class integers one uses the procedure mpz_gcd. This procedure takes three arguments of type mpz_t—not mpz_class. The mpz_class objects contain an mpz_t type value internally and to access that internal value one uses the get_mpz_t() method. Here’s how this all works.

First, we set up our variables:

mpz_class a = "47825100"; mpz_class b = "55431225";

mpz_class d; // place to hold the answer

Then we call the mpz_gcd method like this:

mpz_gcd(d.get_mpz_t(), a.get_mpz_t(), b.get_mpz_t());

Now d holds the gcd of a and b.

An object of type gmp_randclass is a random number generator. This object offers a choice of pseudo random number generator algorithms and allows the user to set the seed. For example, to create a new random number generator with a default algorithm and seeded from the system clock, we write this:

gmp_randclass X(gmp_randinit_default); X.seed(time(0));

To extract a random value from the generator, we use the get_z_bits method and specify the size (in bits) of the result. For example, X.get_z_bits(100) returns a 100-bit random integer.

Here is a program that illustrates these ideas.

Program 13.1: A program to illustrate the use of the GMP package.

1 #include <gmpxx.h>

2#include <iostream>

3

4using namespace std;

5

6int main() {

7mpz_class a, b;

8

9 a = "54098745908347598037452";

10 b = "44523409864";

11

12cout << "a = " << a << endl;

13cout << "b = " << b << endl;

14cout << "a*b = " << a*b << endl;

15cout << "a/b = " << a/b << endl;

16cout << "a%b = " << a%b << endl;

17cout << "a+b = " << a+b << endl;

18cout << "a-b = " << a-b << endl;

19cout << "b-a = " << b-a << endl;

272

C++ for Mathematicians

20

21mpz_class d;

22mpz_gcd(d.get_mpz_t(), a.get_mpz_t(), b.get_mpz_t());

23cout << "gcd(a,b) = " << d << endl;

24

25

26cout << "Is a < b? " << ((a<b) ? "Yes" : "No");

27cout << endl;

28

29mpq_class r(a,b);

30r.canonicalize();

31

32 cout << "As a rational number, a/b is " << r << endl;

33

34mpq_class q(-1.125);

35cout << "q = " << q << endl;

36

37gmp_randclass X(gmp_randinit_default);

38X.seed(time(0));

39cout << "Here’s a random number: " << X.get_z_bits(100) << endl;

40

41return 0;

42}

Compiling this program can be tricky. The computer needs to find the following items,

The header file gmpxx.h, and

The libraries1 gmp and gmpxx.

If the GMP header files are installed in a standard location, no special steps are required for the compiler to locate the header file gmpxx.h. No special steps are needed if the file gmpxx.h resides in the same directory (folder) as the program that #includes it. Otherwise, the compiler needs to be informed of the header file’s location. The precise manner in which this is done can vary between compilers. For example, with the g++ compiler (a popular choice), one specifies the directory in which gmpxx.h resides with the -I command line option. (See the full example below as well as Appendix A.)

It is also necessary to tell the compiler to use the libraries gmp and gmpxx (these are distinct from the header file gmp.h). For example, with the g++ compiler, this is accomplished with a pair of -l command line options (see below). In the case where these libraries are installed in a nonstandard location, it may be necessary to tell the computer where to find these libraries. This is accomplished with the -L command line option in g++.

For example, to compile the gmp-tester.cc program (Program 13.1) above, I use the following command,

g++ gmp-tester.cc -I/sw/include -L/sw/lib -lgmp -lgmpxx

1A library contains compiled code needed by the GMP package.

Here is the output of the program.

Using Other Packages

273

On my computer, the gmpxx.h header file is in the directory /sw/include (specified by the -I option), the libraries are located in /sw/lib (specified by the -L option), and the gmp and gmpxx libraries are named explicitly (by the pair of -l options).

 

 

a =

54098745908347598037452

 

 

 

 

 

 

 

b =

44523409864

 

 

 

 

 

a*b

=

2408660637205753086401397418226528

 

 

 

 

 

a/b

=

1215062953929

 

 

 

 

 

a%b

=

4181881796

 

 

 

 

 

a+b

=

54098745908392121447316

 

 

 

 

 

a-b

=

54098745908303074627588

 

 

 

 

 

b-a = -54098745908303074627588

 

 

 

 

 

gcd(a,b) = 4

 

 

 

 

 

Is a < b? No

 

 

 

 

 

As a rational number, a/b is 13524686477086899509363/11130852466

 

 

 

 

 

q = -9/8

 

 

 

 

 

Here’s a random number: 521322704085186435455013754888

 

 

 

 

 

 

 

13.2Linear algebra

13.2.1 Two-dimensional arrays in C++

One-dimensional arrays in C++ are mildly difficult to use. One needs either to give the explicit size of the array in advance (e.g., long vals[10];) or else to declare the array via a pointer (long *vals;), allocate space (vals = new long[n];), and then release the memory when finished (delete[] vals;). We need to remember that the first element of an array is indexed as vals[0] and there is no protection against accessing data beyond the bounds of the array. There is no convenient way to extend the size of the array. The vector class template solves many of these problems.

It is possible to use multidimensional arrays in C++, but the difficulties are compounded. We describe such arrays here briefly in order to convince you that you do not want to use these tricky structures and, instead, will avail yourself of some of the options we discuss below.

To declare a two-dimensional array of a given size, we use a statement such as this:

long vals[4][10];

This establishes a variable named vals as a 4 × 10-array of long integer values. Please note that the following statement is incorrect: long vals[4,10];. Unfortunately, it is valid C++ (don’t bother trying to figure out what it does) so the compiler won’t catch this error.

274

C++ for Mathematicians

To access an element of this array, we use the syntax vals[i][j] where i is between 0 and 3, and j is between 0 and 9. The expression vals[i,j] is incorrect.

It is possible to declare three- (or higher-) dimensional arrays. For example,

long vals[4][10][19];

declares vals to be a 4 ×10 ×19-array of long values. The i, j,k-entry of this array is given by vals[i][j][k] where i, j,k have the expected constraints.

Passing multidimensional arrays to procedures is possible, but the syntax is tricky. Procedures that receive such arrays need to specify the size of the array they expect to receive.

Fortunately, there are better alternatives. The two that we present have the added advantage of providing linear algebraic capabilities (matrix multiplication, eigenvalue calculation, etc.).

13.2.2 The TNT and JAMA packages

The U.S. government’s National Institute of Standards and Technology (NIST) has created a pair of packages for working with one-, two-, and three-dimensional arrays and to perform standard linear algebra functions thereon. The packages are the Template Numerical Toolkit (TNT) and the C++ Java Matrix Library (JAMA).2

These are available for free download from the following Web site,

http://math.nist.gov/tnt/index.html

You need to download the TNT and JAMA packages separately. Be sure to download the documentation as well; this consists of a collection of Web pages that describe the various classes and procedures. (We describe TNT version 1.2.4 and JAMA version 1.2.2.)

Installation is easy because these packages are simply collections of .h header files. They can be copied to any convenient location on your computer (including, e.g., the directory containing your program that uses these files).

The most important classes in the TNT package are Array1D (for vectors) and Array2D (for matrices3). To use these, we need the directive #include "tnt.h" and the statement using namespace TNT; at the beginning of our program. The classes and procedures in the TNT class are in their own namespace. If we did not use this statement, then we would need to prepend TNT:: to the names of TNT classes and procedures (e.g., TNT::Array2D in lieu of Array2D).

To declare an array of elements of type, say, double, we use statements like these:

Array2D<double> M(5,10);

Array1D<double> v(9);

Array2D<double> X;

2This library was developed first for the Java programming language and subsequently ported to C++.

3You may discover that the TNT package includes classes called Matrix and Vector. Do not use these. They are deprecated classes; this means they are obsolete and their inclusion in the package is only to support people who used older versions of TNT.

Using Other Packages

275

Here, M is a 5 × 10-array (matrix) of double values, v is a length-9 array (vector), and X is an as-yet unsized matrix.

To access elements of an array we use the usual C++ conventions. For the variables M and v above, the syntax is as follows,

M[i][j] where 0 ≤ i < 5 and 0 ≤ j < 10, and

v[k] where 0 ≤ k < 9.

To learn the size of an array, the methods dim1() and dim2() give the number of rows and columns (if more than one-dimensional) in the array.

Read this carefully: The assignment operator for TNT array classes (e.g., B=A;) works in a manner that is different from other classes we have encountered thus far. After this assignment not only are A and B arrays of the same size and not only do they hold the same values, they now refer to the exact same data. In other words, modifications to one of A or B results in change in the other. (We say that A and B provide different views to the same underlying data.) If we wish to make an independent copy of A in B, we need to use the copy method like this:

B = A.copy();

Here is a program that illustrates the difference between the statements B=A; and

B=A.copy();.

Program 13.2: Assignment versus copying in the TNT package.

1 #include <iostream>

2#include "tnt.h"

3

4 using namespace std;

5using namespace TNT;

6

7int main() {

8Array2D<double> A(3,3), B, C;

9

10for (int i=0; i<3; i++)

11for (int j=0; j<3; j++)

12A[i][j] = (i+1) + 0.1*(j+1);

13

 

 

14

cout << "Original matrix A:" << endl << A << endl;

15

 

 

16

B = A;

// Now B and A share data

17

C = A.copy();

// C is an independent copy of a

18

 

 

19B[1][1] = 888;

20A[0][2] = 999;

21

22cout << "Now A is this " << endl << A << endl;

23cout << "and B is this " << endl << B << endl;

24cout << "and C is this " << endl << C << endl;

25

26return 0;

27}