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

Beginning Python (2005)

.pdf
Скачиваний:
158
Добавлен:
17.08.2013
Размер:
15.78 Mб
Скачать

Numerical Programming

Occasionally, you might need to convert between characters and their numeric values. Python provides the built-in function ord to convert a single character to its numeric code and the function asc to convert back from a numeric code to a character. The numeric code must be between 0 and 255.

Strictly speaking, this code is not ASCII, as ASCII only goes up to 127. However, the first 127 values converted by ord and asc are ASCII code values.

If you are a Usenet regular, you are probably familiar with the rot13 cipher. It’s not particularly secure; all it does is rotate letters of the alphabet 13 positions forward, wrapping around from “z” to “a”. Using chr and ord functions, it’s not hard to implement in Python:

def rot13_character(character):

# Look up codes for ends of the alphabet. a = ord(‘a’)

z = ord(‘z’) A = ord(‘A’) Z = ord(‘Z’)

code = ord(character)

#Rotate lower-case characters. if a <= code <= z:

code = a + (code - a + 13) % 26

#Rotate upper-case characters. elif A <= code <= Z:

code = A + (code - A + 13) % 26

#Leave other characters alone. else:

pass

return chr(code)

def rot13(plaintext):

# Loop over letters in the text. ciphertext = “”

for character in plaintext:

ciphertext += rot13_character(character) return ciphertext

The program is composed of two functions. The first, rot13_character, applies rot13 to a single character. If it’s an uppercase or lowercase letter, it is rotated 13 places; otherwise, it is left alone. (In case you are not familiar with the remainder operator, %, it is described in the next section.) The main function, rot13, takes the message to be coded (the “plaintext”) and creates the encoded message (the “ciphertext”) by rotating each letter.

Save the preceding code into a module file named rot13.py. In Python, import the module, and try it out:

>>>import rot13

>>>message = rot13.rot13(“This is a TOP-SECRET encoded message.”)

>>>print message

Guvf vf n GBC-FRPERG rapbqrq zrffntr.

411

TEAM LinG

Chapter 19

Rot13 has the nice property that it is its own inverse: To decode a rot13-encoded message, you just apply rot13 to it again:

>>> print rot13.rot13(message)

This is a TOP-SECRET encoded message.

Mathematics

In addition to the usual complement of arithmetic operations, Python includes some handy built-in math functions, and a math module that provides other commonly used functions. Coverage of arithmetic operators may seem obvious, but you should also understand some subtle points about how Python handles certain numeric types.

Arithmetic

Python provides the normal arithmetic operators + (addition), _ (subtraction), * (multiplication), and / (division) for numerical types. You can mix numerical types when using these operators; Python automatically chooses the more flexible type for the result:

>>>i = 10

>>>f = 6.54

>>>print i + f 16.54

When adding an integer, i, and a floating-point number f, Python chose a float for the result.

These operators all have special forms for updating the values of variables, written by adding an equals sign right after the operator. Instead of writing

>>>total = total + 6

>>>coefficient = coefficient / 2

you can simply write

>>>total += 6

>>>coefficient /= 2

and so forth.

Be careful when dividing two integers (whether they are int or long objects). Python always uses an integer type for the result, even if the “correct” result is fractional. Don’t get caught by computations like this one:

>>> print 10 / 3 3

412

TEAM LinG

Numerical Programming

Oops! The “correct” answer is three and one third, or 3.3333, but Python uses an integer for the result, rounding down to three. This doesn’t work either:

>>>quarter = float(1 / 4)

>>>print quarter

0.0

What you’ve asked Python to do here is to divide two integers, and then to convert the result to a float. What you really want is division of the numbers as floating-point values. Adding a decimal point to either number does the trick: That makes the number a float literal, so Python uses float for the quotient:

>>>quarter = 1.0 / 4

>>>print quarter

0.25

Similarly, if you are writing a function that takes the average of two numbers, you probably want the result as a float even if both arguments are integers. Use the float constructor to do the conversion:

>>> def average(x, y):

 

...

return float(x

+ y) / 2

...

 

 

The exponentiation operator ** is used previously in examples. It, too, works for integer and floatingpoint values. The function that follows uses it to compute compounded interest. The function returns the amount of money you would have if you put starting_balance in a bank account with APR annual_rate and waited for years:

>>> def compound(starting_balance, annual_rate, years):

...

return starting_balance * ((1 + annual_rate) ** years)

...

 

Ten grand in the bank at 4 percent APR for a century yields:

>>> print compound(10000, 0.04, 100) 505049.481843

That’s half a million bucks. Start saving now.

Floor Division

Python provides another division operator, called floor division, which explicitly rounds down the quotient to an integer, like the default division behavior for int and long. Floor division is represented by //. You can use it with float objects as well: for instance, 6.6//3.0 evaluates to 2.0.

When you divide two integers, it’s a good idea to use floor division when that’s what you mean, or explicitly to convert one of the arguments to a float otherwise. This will prevent you from getting burned by surprising division results.

413

TEAM LinG

Chapter 19

Also useful is the remainder operator %. It’s like floor division, but instead of returning the quotient, it returns the remainder. Using it, you can format a number of months into whole years and remaining months:

>>> def format_months(months):

...

print “%d years, %d months” % (months // 12, months % 12)

...

 

>>> format_months(100) 8 years, 4 months

Built-in Math Functions

A few very common mathematical functions are available as built-in functions. The simplest is abs, which returns the absolute value of a number. The number that abs returns is the same type as the number you pass it:

>>> print abs(-6.5) 6.5

Also useful are min and max, which return the smallest or largest of several values. You can call them either with several numeric arguments or with a single argument that is a sequence (such as a list or tuple) of numbers. The values needn’t all be the same type:

>>>print min(6, 7, 2, 8, 5)

2

>>>print max([0, 43.5, 19, 5, -6])

43.5

The round function rounds a floating-point value to a specified number of digits. This is similar to the behavior you saw before in the %f conversions, except the result is not a string but rather another floatingpoint number with which you can perform further computations. Specify the number to round, and the number of decimal places you want to keep:

>>> print round(1234.56789, 2) 1234.57

You can even specify a negative number of decimal places, which rounds to that multiple of 10:

>>> print round(1234.56789, -2) 1200.0

Lastly, the sum function adds numbers in a sequence. Together with range, you can compute the sum of the first 100 positive integers:

>>> print sum(range(1, 101)) 5050

Suppose in your Python programming class you got a 96 percent and 90 percent on the two homework assignments, a perfect score on the final project, and an 88 percent on the final exam. What’s your

414

TEAM LinG

Numerical Programming

average for the class? Of course, you would write a Python function to compute it. The function uses sum and computes the mean, or average, value of a sequence of numbers:

>>> def mean(numbers):

...

if numbers:

...

return float(sum(numbers)) / len(numbers)

...

else:

...

raise ValueError, “no numbers specified”

...

 

>>>

print mean([96, 90, 100, 88])

93.5

It’s a good idea to make sure that the sequence of numbers isn’t empty, to avoid dividing by zero. In this case, the function raises an exception if the sequence is empty.

The math Module

The math module contains the standard transcendental functions listed here. All these functions take float arguments and return float values:

square root: sqrt

exponentiation: exp

logarithms: log (natural logarithm), log10 (base 10 logarithm)

trigonometric functions: sin, cos, and tan; arguments are in radians

inverse trigonometric functions: asin, acos, and atan; results are in radians

hyperbolic functions: sinh, cosh, and tanh

A few other useful math functions are included:

hypot(x, y) is equivalent to sqrt(x ** 2 + y ** 2)

atan2(x, y) is like atan(x / y) but gets the quadrant right and handles a zero denominator

floor and ceil are the standard floor and ceiling functions; their results are integers but represented as float values

The math package also contains the constants pi and e.

Here’s some sample code that uses the math module. It will give you flashbacks to your freshman physics class. It’s a function that computes the time of flight and range of a projectile launched into the air (such as a cannonball), neglecting friction. Examine it at least long enough to understand how the Python code works. Pay attention to how sin, cos, and pi are imported from math, which saves you from having to refer to them as math.sin and so on. It’s a handy technique for commonly used functions. Note also how carefully the units used in the arguments and results are documented. Many failed rocket launches attest to the importance of this practice.

415

TEAM LinG

Chapter 19

from math import sin, cos, pi

def trajectory(velocity, angle):

“””Compute time of flight and range of a projectile.

For a projectile with initial ‘velocity’ in meters/sec launched at ‘angle’ from horizontal in degrees, returns time of flight in sec and range in meters, neglecting friction.”””

#Gravitational acceleration in meters/sec^2. g = 9.8

#Convert ‘angle’ to radians.

angle = angle * pi / 180

#Compute horizontal and vertical components of velocity. v_h = velocity * cos(angle)

v_v = velocity * sin(angle)

#Compute the time of flight and range.

tof = 2 * v_v / g range = tof * v_h return tof, range

Suppose you throw a ball into the air at 40 m/sec (about 90 mph) at a 45° angle. How long will it stay in the air, and how far away will it land? Save the preceding code into a file named ballistic.py, and then call the function like this:

>>>from ballistic import trajectory

>>>tof, range = trajectory(40, 45)

>>>print “time of flight: %.1f sec, range: %.0f meters” % (tof, range) time of flight: 5.8 sec, range: 163 meters

Complex Numbers

A complex number is the sum of a real number and an imaginary number. In case you need a refresher, an imaginary number is a multiple of the imaginary unit, which is the square root of –1. Mathematicians (and math teachers) usually use the symbol i for the imaginary unit, while engineers often use j.

In Python, an imaginary number is written as a number followed by j (with no intervening spaces):

>>> imaginary_number = 16j

To create a complex number, add (or take the difference of) a real number and an imaginary number:

>>> complex_number = 6 + 4j

Python stores the complex number as a single object, whose type is complex:

>>>print complex_number (6+4j)

>>>print type(complex_number) <type ‘complex’>

416

TEAM LinG

Numerical Programming

If you prefer, you can use the complex constructor to construct complex number objects. This assignment is equivalent to the preceding one:

>>> complex_number = complex(6, 4)

Let’s make sure that 1j is really the imaginary unit:

>>> print 1j ** 2 (-1+0j)

This verifies that j2 is in fact –1, and also demonstrates that the result of an arithmetic operation involving complex values is itself a complex, even if the result happens to be a real number (that is, has a zero imaginary part).

You can’t write j by itself to represent the imaginary unit. You must write 1j. By itself, j represents the variable named “j.”

Both the real and imaginary parts of a complex object are stored as floating-point values, even if you specified them as integers. Remember that 1/3 in Python returns zero? Not so for complex numbers:

>>> print (1+0j)/3 (0.333333333333+0j)

Arithmetic works for complex numbers as you would expect, and you can mix int, long, float, and complex in the same expression:

>>> print 2 * (10 + 3j) * (6.5 - 4j) / (1 - 1j) + 30L (127.5+56.5j)

A few other operations round out Python’s handling of complex numbers. First, the mathematical operations Re and Im return the real and imaginary parts of a complex number, respectively. These are provided in Python by attributes named real and imag that every complex object has. The value of each is a float:

>>>x = 5 - 6j

>>>print x.real

5.0

>>>print x.imag

-6.0

You saw before that the built-in abs function returns the absolute value of an int, long, or double object. For complex numbers, it returns the magnitude, which is the square root of the sum of the squares of the real and imaginary parts. You can verify this by using the hypot function discussed previously:

>>>print abs(x) 7.81024967591

>>>import math

>>>print math.hypot(x.real, x.imag) 7.81024967591

417

TEAM LinG

Chapter 19

Precision of Complex Numbers

Let’s verify the famous and very fundamental mathematical identity eiπ + 1 = 0:

>>> print cmath.exp(1j * cmath.pi) + 1 1.22460635382e-016j

What’s this? It’s a complex number with real part of zero and imaginary part approximately 1.225 × 10_16. That’s close, but not quite equal to zero.

Python stores both the real part and the complex part with the same precision as a float value, about 16 digits on most systems. That means Python’s representation of eiπ is equal to –1 only to about 16 digits. Therefore, you shouldn’t be surprised if the result after adding +1 is off by about 10–16.

Finally, every complex object has a method conjugate, which returns the complex conjugate. This is the complex number with the same real part and negated imaginary part. Keep in mind that while real and imag are attributes (you don’t call them), conjugate is a method (you must call it):

>>> print x.conjugate() (5+6j)

The transcendental functions in the math package work only on and return float values. For instance, you can’t actually take the square root of –1 to obtain 1j:

>>> print math.sqrt(-1)

Traceback (most recent call last):

File “<interactive input>”, line 1, in ? ValueError: math domain error

That’s a shame, because square roots and most of the other functions in math can be defined on complex numbers, too. Fortunately, Python provides a parallel module named cmath, which contains versions of the same functions that operate on and return complex objects. Its version of the square root function can handle –1:

>>>import cmath

>>>print cmath.sqrt(-1)

1j

Arrays

You’ve learned how to perform computations with individual numbers, be they integers, floating-point numbers, or even complex numbers. What if you want to perform computations on many numbers? A group of numbers is typically arranged into an array. In this section, you will learn different ways of implementing arrays in Python.

Keep in mind that arrays may be multidimensional. If you arrange numbers in a row, you have a onedimensional array. A vector in linear algebra is an example; another is a list of daily closing prices of your

418

TEAM LinG

Numerical Programming

favorite stock. You can also arrange your numbers on a rectangular grid, to produce a two-dimensional array. A grayscale image is often represented as a two-dimensional array, where each value is the lightness of one pixel in the image. In some applications, you may want to arrange your numbers into higherdimensional arrays as well.

You’ve already seen one technique for constructing arrays in Python, when you wrote the mean function earlier. That function takes a sequence of numbers (of arbitrary length) and computes the numbers’ mean. You can think of this sequence of numbers as an array and can think of mean as a function that acts on an array. You can invoke the function with a list of numbers, but it works with any sequence type, including tuples. These built-in types are the simplest way of building arrays.

Let’s take another example of a function that operates on an array. You already wrote a function that computes the mean of an array of numbers. Now write a function that computes the standard deviation. To remind you, the standard deviation is an indication of how much the numbers vary among themselves. If they’re all almost the same, the standard deviation will be small, whereas if they are all over the place, the standard deviation will be large. The formula for the standard deviation that you will use is shown as follows:

 

1

N

v =

!xi2 - n2

 

N i = 1

Here x1,..., xN are the numbers in the array, is their mean, and N is the length of the array.

You could implement a standard deviation function several different ways. Here’s one of them:

from math import sqrt

def stddev(numbers): n = len(numbers) sum = 0

sum_of_squares = 0 for number in numbers:

sum += number

sum_of_squares += number * number

return sqrt(sum_of_squares / n - (sum / n) ** 2)

This function loops over the numbers to compute their sum of squares. Simultaneously, it computes their sum, as it needs that to compute the mean. The last line computes the standard deviation according to the preceding formula. You might have noticed that the function uses number * number when computing the sum of squares instead of number ** 2; that’s because squaring a number by multiplying it by itself is faster than using the general exponentiation operator.

Lists or Tuples?

Which should you use for arrays: lists or tuples? Remember that lists can be modified, whereas tuples cannot. Therefore, if you need to add to, remove from, or change the array, use a list. While you can perform these operations on a tuple by creating a new tuple with numbers added, removed, or changed, this is more difficult to code and often runs more slowly. For fixed sequences of numbers, you can use tuples.

419

TEAM LinG

Chapter 19

Watch stddev in action. Remember that it takes one argument, a sequence of numbers (not several numerical arguments):

>>> print stddev((5.6, 3.2, -1.0, 0.7)) 2.50137462208

Think for a moment about some advantages and drawbacks of using lists of numbers for arrays:

The elements of a Python list need not be of the same type. You can create a list for which some elements are int, float, long, and double, or other objects like strings or even other sequences. For some applications, this is very handy. For instance, you may want to store None in a sequence to indicate that a value is not known. For other applications, it’s important to make sure that all of the values in an array are of the same type. In that case, you’ll have to write extra code to ensure this.

Lists are single-dimensional, which makes them natural for expressing one-dimensional arrays. You can create two-dimensional arrays as lists of lists and higher-dimensional arrays analogously, but this can get complicated.

Lists are a standard part of Python. They’re always available (you don’t even have to import a module), and you already know how to use them.

Lists can be pickled. That makes it easy to store your list in a file for later use.

Internally, Python represents each element in a list as a separate object. Therefore, if you have a list of a million numbers (not at all unusual in many fields), you force Python to keep track of 1,000,001 objects: the list itself and all of its elements. This both wastes a lot of memory and makes Python work pretty hard whenever you access or modify the array.

This last point is a major limitation in many types of numerical work. To address it, you can use one of two other array implementations that store numbers more efficiently.

The array Module

The Python standard library has just the ticket: a module array for one-dimensional arrays of numbers. The array type in this module stores numbers all together in memory as one object, subject to the constraint that all of them must be of the same type. The numerical types supported by array are not the same as Python’s numeric types. (In fact, they correspond to the numerical types in the C language.) An array can store numbers equivalent to Python’s int and float, as well as integers of other sizes, and floating-point numbers of other precisions. (An array can store long values, but not arbitrarily large ones, and cannot store complex values at all.)

When you create an array, you have to specify the numerical type to store in the array. The type is specified by a single character. To store numbers as Python int objects, use “l”; for float use “d”. (There are other options available; see the documentation for the array module for a list of them.) If you don’t specify anything else, you’ll get an empty array:

>>>import array

>>>a = array.array(“l”)

>>>print a

array(‘l’)

420

TEAM LinG