COMO 102 : Scientific Programming, Lecture Notes 2003 Computational Modelling 102 (Scientific Programming) Lecture Notes Dr J. D. Enlow Last modified August 25, 2003. 1 Contents 1 Preliminary Material 5 1 Introduction 1.1 Course Information . . . . . . . . . . . . . . . . . . 1.1.1 Aims of the Course . . . . . . . . . . . . . . 1.1.2 Teaching Methods . . . . . . . . . . . . . . 1.2 Introduction to Scientific Programming . . . . . . . 1.2.1 Introduction to Numerical Methods . . . . . 1.2.2 Numerical Solutions versus Exact Solutions 1.3 Introduction to MATLAB . . . . . . . . . . . . . . 1.3.1 Brief History of MATLAB . . . . . . . . . . . . . . . . . . 6 6 6 6 6 9 9 10 10 . . . . . . . . . . . . 11 11 12 12 13 13 14 15 15 15 16 16 16 . . . . . 18 18 19 20 20 23 4 MATLAB’s M-files 4.1 Script m-files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2 Function m-files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.1 plotData - A More Complicated Example . . . . . . . . . . . 25 25 26 27 2 Revision: Linear Algebra 2.1 Vectors . . . . . . . . . . . . . . . . . . . . 2.1.1 Examples of Basic Operations . . . 2.1.2 Examples using MATLAB notation 2.1.3 Linear Combinations . . . . . . . . 2.1.4 Representing Vectors as Arrows . . 2.1.5 The Dot Product . . . . . . . . . . 2.1.6 Unit Vectors . . . . . . . . . . . . . 2.1.7 Orthogonal Vectors . . . . . . . . . 2.1.8 Orthonormal Vectors . . . . . . . . 2.2 Matrices . . . . . . . . . . . . . . . . . . . 2.2.1 Matrices and Vectors in MATLAB 2.2.2 Matrix Operations . . . . . . . . . 3 Graphics in MATLAB 3.1 Vectorisation and Array Operators . 3.1.1 Array Operators in MATLAB 3.2 Plotting in Two Dimensions . . . . . 3.2.1 The Plot Command . . . . . 3.2.2 MATLAB’s Help Files . . . . 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 COMO 102 : Scientific Programming, Lecture Notes 2003 4.2.2 Use of Subfunctions . . . . . . . . . 4.3 Flow Control: If statements and for loops . 4.3.1 Relational Operators . . . . . . . . 4.3.2 The if statement in MATLAB . . 4.3.3 Using for loops in MATLAB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 29 30 32 32 5 Differential Equations: Euler’s Method 5.1 Derivatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2 Introduction: ODEs . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.1 Example: Newton’s Law of Motion as an ODE . . . . . . . 5.2.2 Example: Newton’s Law of Cooling . . . . . . . . . . . . . 5.3 Strategy for the Numerical Methods . . . . . . . . . . . . . . . . . 5.4 Euler’s Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.4.1 Euler’s Method is a series of Straight Line Approximations . . . . . . . 33 33 33 34 35 36 37 37 6 Programming in MATLAB: The Asteroid Problem 6.1 An Asteroid under Earth’s Gravity . . . . . . . . . . 6.2 One Step of the Asteroids Motion . . . . . . . . . . . 6.2.1 Calculating the Acceleration Vector . . . . . . 6.2.2 Calculating the New Position and Velocity . . 6.2.3 An Algorithm to Solve the Asteroid Problem . 6.2.4 Errors . . . . . . . . . . . . . . . . . . . . . . 6.3 Multiple Planets . . . . . . . . . . . . . . . . . . . . 6.3.1 Three Planets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 38 39 40 40 41 42 43 44 7 Various Topics 7.1 Revision . . . . . . . . . . . . . . . . . . . . . . 7.1.1 Strategy for the Numerical Methods . . 7.1.2 Euler’s Method . . . . . . . . . . . . . . 7.2 ode23 and ode45 . . . . . . . . . . . . . . . . . 7.2.1 ode23 . . . . . . . . . . . . . . . . . . . 7.2.2 ode45 . . . . . . . . . . . . . . . . . . . 7.3 File Input and Output (“I/O”) in MATLAB . . 7.3.1 ASCII and MAT files . . . . . . . . . . . 7.4 Machine Precision . . . . . . . . . . . . . . . . . 7.4.1 Consequences . . . . . . . . . . . . . . . 7.4.2 Example: Modulus of a complex number . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 45 45 46 47 47 47 48 48 49 49 51 . . . . . . . . 52 52 52 52 53 53 53 54 54 8 Programming in C 8.1 Basic Concepts . . . . . . . . . . 8.1.1 Statements . . . . . . . . 8.1.2 Data and variables . . . . 8.1.3 Declarations . . . . . . . . 8.1.4 Pointers . . . . . . . . . . 8.1.5 Arrays . . . . . . . . . . . 8.1.6 Functions . . . . . . . . . 8.1.7 Local and global variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 COMO 102 : Scientific Programming, Lecture Notes 2003 8.1.8 Parameters . . . . . . . . . . . . . . . . . 8.2 A Short History of the Language C . . . . . . . . 8.3 A Few Characteristics of the Language C . . . . . 8.3.1 Compiler-based . . . . . . . . . . . . . . . 8.3.2 Extensive Use of Libraries . . . . . . . . . 8.3.3 Shorthand for symbols that are used often 8.4 Libraries . . . . . . . . . . . . . . . . . . . . . . . 8.5 A Sample C Program . . . . . . . . . . . . . . . . 8.5.1 Source Code . . . . . . . . . . . . . . . . . 8.5.2 Compilation and Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 55 56 56 56 57 57 58 58 58 9 Programming in C Part II 9.1 Variables in C . . . . . . . . . . . . . . . . . . . . . 9.1.1 Variable Declaration . . . . . . . . . . . . . 9.1.2 Printf . . . . . . . . . . . . . . . . . . . . . 9.1.3 Formatting Arguments of Printf . . . . . . . 9.2 Parameters in Subroutines . . . . . . . . . . . . . . 9.3 Pointers . . . . . . . . . . . . . . . . . . . . . . . . 9.3.1 Allowing Permanent Changes in Parameters 9.3.2 Pointer Values . . . . . . . . . . . . . . . . . 9.3.3 Pointers are Useful but *DANGEROUS*! . 9.3.4 Making Pointers Safer . . . . . . . . . . . . 9.3.5 Some Friendly Advice . . . . . . . . . . . . 9.4 Bug Finding and Indenting . . . . . . . . . . . . . . 9.4.1 lclint . . . . . . . . . . . . . . . . . . . . . . 9.4.2 indent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 59 59 60 60 61 62 62 63 63 64 65 65 65 65 10 Programming in C Part III 10.1 Arrays . . . . . . . . . . . . . . . . . . . . . 10.1.1 Passing Arrays to Subroutines . . . . 10.2 Writing output to a file: fprintf . . . . . . . 10.2.1 Exporting Data from C to MATLAB 10.3 Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 67 68 70 71 73 . . . . . . . . 74 74 74 75 76 77 77 78 79 . . . . . . . . . . . . . . . 11 Programming in C Part IV 11.1 The Comotank Client-Server Framework . . . . . 11.1.1 The Rules . . . . . . . . . . . . . . . . . . 11.1.2 An Empty Client . . . . . . . . . . . . . . 11.1.3 The Controlling Functions in More Detail 11.1.4 A Simple Tank . . . . . . . . . . . . . . . 11.1.5 Compiling and Testing . . . . . . . . . . . 11.1.6 Utilities Available . . . . . . . . . . . . . . 11.1.7 Tankview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Chapter 1 Preliminary Material Lecturer Dr John Enlow, Room 518, Science III ([email protected] ). Course structure: Each week consists of: • One lecture (in Lab B, Monday at 11am), • One one-hour tutorial (in Lab A, Wednesdays at 11am). • One three-hour laboratory (in Lab A, Thursdays from 9am to 12pm). The laboratory exercise is due at 1pm on Friday. Assessment: The internal assessment is comprised of the mid-semester test (40%) and the weekly exercises (60%). Your final grade is either your final exam score, or one third of your internal assessment plus two thirds of your final exam score, whichever is greater. 2 1 Final Grade = M AX EXAM, EXAM + I.A. 3 3 Computer Packages Used The first half of this course will use a commercial scientific package called MATLAB. The second half will focus on C programming, and you will have access to the Linux server COMO to write and run your programs on. These tools are available from Lab A in the mathematics department. If you wish to work from home you can purchase a student version of MATLAB from Hoare research (www.hrs.co.nz), or from the University Bookshop, and download a free Windows C compiler and integrated development environment from http://www.bloodshed.net/devcpp.html. Books There is no prescribed text for this course. 5 Lecture 1 : Introduction Chapter 1 1.1 Course Information Introduction 1.1.1 Aims of the Course 1. To provide a detailed introduction to programming in MATLAB. 2. To briefly introduce programming in C from within a Unix environment. 3. To reinforce ideas from other mathematics papers. 4. To improve your problem solving, modelling and computer skills. 1.1.2 Teaching Methods The course is primarily hands-on, with four of the five contact hours per week on computers in Lab A. You will get one small exercise to be done in the tutorial each week, for which solutions will be provided, and one lab exercise per week which will count towards your internal assessment. You are expected to spend 12 hours per week on this course, consisting of the lectures, tutorials and labs as well as several hours per week working on your own. 1.2 Introduction to Scientific Programming The scientific programming taught in this course centers on the implementation of numerical methods to solve real-world problems. Many languages are available to the scientific programmer for tackling a problem, and the choice of language will often depend on the programmers background and need for collaboration. We use the word language loosely here, including scripting languages as used in Mathematica and MATLAB. Key features desired in a language are: • Accuracy. When solving a problem on computer numerical errors are often of critical importance. Some languages have poor mathematical routines and/or low accuracy in their data types. This may depend partly on the compiler. • Speed. Many high-end ‘nice’ languages, such as Java and Mathematica, provide a long list of advantages over traditional languages, but at the cost of speed. A complicated numerical method implemented in C or MATLAB can be orders of magnitude faster than a Java implementation. Other factors can come into play here - is the language interpreted? Does the compiler contain optimizations for your type of computer? 6 7 COMO 102: Lecture 1, 2003 • Graphics. Graphical representation of solutions are an important part of the modelling process. Does the language have convenient methods or libraries for presenting data? Must you rely on low-level routines? Are standard graphics libraries available (e.g. OpenGL)? Can you easily export data for use in highlevel software like Mathematica? • Libraries / Add-on Packages. What mathematical functionality does the language come with? Do you have to implement standard mathematical data structure types yourself (e.g. vectors, matrices, complex numbers)? • Portability. Will your program work on other types of computers? As a scientific programmer you may develop your program on a low-end PC, then wish to run it on a powerful super-computer. Of course there are many other considerations, such as whether the code is easy to read and reusable, whether the language is object orientated, the overhead in producing code, whether you can produce a nice GUI for your program (if it is to be used by others), oddities in the language - do you spend time hunting down obscure bugs that are due to inconsistent language features? Some languages that you may consider are summarized below: Language or Package Mathematica MATLAB JAVA C++ C FORTRAN Design Symbolic Numerical O.O. O.O. Numerical Numerical Numerical Speed Slow Very fast Slow Fast Very fast Fastest GUI creation None Poor Good Good Average None Graphics Capabilities Good Good Average Average Average Poor Ease of use Excellent Good Average Average Poor Poor • Mathematica. Primarily symbolic - relatively slow at numerical work. A very good tool for developing the theory and which algorithms to use, not very good for implementation of the algorithm. Nice graphics, good front-end, easy to use but with a few quirks. Available for Windows, MacOS, and UNIX environments. Poor facilities for production of a graphical user interface. • MATLAB. Primarily numerical - very fast at some matrix operations, extensive inbuilt mathematics routines, excellent handling of sparse matrices, good graphics capabilities. Has ability to write a GUI, but resulting interface is fairly crude, very slow and crash-prone. Not a very elegant system to work with. Function names are often non-obvious. Available for all major platforms. • Java. Heavily object orientated, comes with a large overhead. Portable, flexible, nice integration with web page interfaces. Not suitable for serious numerical work. • C++. Full-featured, fast for numerical work (provided you don’t go classcrazy), flexible. Available for just about every architecture and operating COMO 102: Lecture 1, 2003 8 system. Many components need to be written ‘from scratch’, however there are resources on the internet (class libraries) for a wide range of tasks, including plotting and numerically solving problems. • C. Very fast. Not object orientated, so code is harder to maintain and less easy to read, but uses less memory and is more compact. Otherwise similar to C++. MATLAB is written in C, along with an enormous number of programs from the Linux kernel to Quake III. Note that C is essentially a subset of C++, so you can use C++ for the user interface and input/output sections of the code and C for the real work (numerical methods) all in one program. • Fortran. An older language developed for engineers. Produces code comparable in speed to C (and generally a bit faster). The language has many frustrating restrictions and can be a nightmare to debug. Still in use by some programmers, particularly engineers, but people learning to write numerical algorithms now often use C/C++ instead. You may find a combination of these languages suits your needs best. E.g.: • Financial forecasting software with a web interface. Java for the front-end web components, C for executing the calculations. • Solving a problem involving a huge amount of data. C for the calculations, MATLAB for graphing the solutions. • A user-friendly package to predict heat-flow in a high-temperature industrial oven. Mathematica for design of the numerical method and model, C++ for producing the user interface, C for the numerical algorithms. There are many other languages out there, but few are as suitable for implementing numerical methods as MATLAB and C. Beware of portability issues, if your code is to be used for longer than a year or two then portability can become a major hassle. This course will primarily use MATLAB, but will also give you a brief introduction to C. It will focus on producing solutions to mathematical models. Your software will generally not be designed for others to use, as we don’t want to spend too much time developing nice interfaces. Writing user interfaces can involve a surprising amount of effort! We will look at problems from a variety of areas. If you have any particular problems from another subject that you’d like to see solved, please let me know, as the course content is reasonably flexible. COMO 102: Lecture 1, 2003 1.2.1 9 Introduction to Numerical Methods Numerical methods are techniques for finding approximate solutions to mathematical problems. Some numerical techniques have been established for a long time. For example Euler is credited with having introduced finite differences in 1768 to approximate derivatives in ordinary differential equations. The finite difference method, one of many methods available, is capable of producing numerical solutions to both ordinary and partial differential equations. Real progress on the development and application of numerical methods is a product of the 20th century. Early contributions of historical importance were made by scientists such as Runge, Richardson, Liebmann, Courant, Friedrichs, von Neumann, among many others. Due to the widespread availability of computers, which are particularly well suited to numerical algorithms, numerical methods are now a key technology for industrial progress in a modern society. The speed and accuracy of modern computers allows the use of simple numerical methods in complicated problems. This greatly increases the ability of a student with only an introductory mathematical background to solve real-world problems. Applications of Numerical Methods A distinctive feature of numerical analysis is its highly multidisciplinary character; it involves research workers from a wide spectrum of interests, for example: • professional numerical analysts, • computer scientists, • mathematicians, • physicists, • chemists, • engineers, • financial modelers. Numerical methods make it possible to theoretically model and simulate phenomena of interest from almost any discipline. 1.2.2 Numerical Solutions versus Exact Solutions Exact solutions: • provide greater information about the system, • allow a deeper understanding of the effects of parameters, and • avoid issues such as numerical error and instability which plague many attempts at numerical solutions. COMO 102: Lecture 1, 2003 10 However, exact solutions are often obtainable only for simple systems. The approximations required to produce an exact solution may make the solution of little interest except as a stepping stone towards a numerical solution! Exact solutions usually require a greater degree of mathematical knowledge and skill than numerical techniques. 1.3 1.3.1 Introduction to MATLAB Brief History of MATLAB • LINPACK and EISPACK developed in mid-1970’s using the (then popular) computer language FORTRAN. Together, LINPACK and EISPACK represented state of the art software for matrix computation. • In the late 1970’s Cleve Molar, one of the designers of LINPACK and EISPACK, wanted students to use the subroutines without having to learn FORTRAN. He developed a crude program to be used as a simple interface. He called it MATLAB, an abbreviation for MATrix LABoratory. • As Cleve visited universities he left copies of MATLAB on their computers. Within a year or two, MATLAB started to catch on within the applied math community. • In early 1983 an engineer, John Little, recognized the potential application of MATLAB to engineering applications, and helped Cleve develop a new version written in C and integrated with graphics. • The MathWorks, Inc. was founded in 1984 to market and continue development of MATLAB. Since then MATLAB has grown into a full-featured package for solving both realworld and theoretical problems, primarily using numerical methods operating on matrices. There are many toolboxes available to extend MATLAB’s functionality, enabling MATLAB to be used to solve a very wide range of problems. The toolboxes provide routines involving everything from statistics and optimization to neural networks and image processing. Lecture 2 : Revision: Linear Algebra Chapter 2 MATLAB is entirely based on matrices. Thus understanding the basics of linear algebra is crucial. These lectures will provide a review of what should be familiar material and use MATLAB examples to illustrate the ideas. Revision: Linear Algebra 2.1 Vectors A vector is an ordered set of numbers arranged as a row or column. The numbers in the vector are known as elements or components. Vectors are usually written enclosed in large round or square brackets, we will use square brackets in this course. Row vectors can be written with or without commas separating the elements. A row vector: ~u = [u1 , u2 , u3 , . . . , un ] A column vector: ~x = x1 x2 .. . xm Recall that the MATLAB function length gives the number of elements in the vector, not the magnitude of the vector! To minimise confusion we will avoid the use of the word length, using magnitude when describing the distance from tail to head of a vector, and dimension when referring to the number of elements in a vector. The magnitude of a vector can be expressed as the square root of the sum of the squares of its elements: |~u| = q u21 + u22 + . . . + u2n . This distance is |~u|, i.e. the magnitude of ~u. ~u Vectors can be used to represent physical quantities that contain directional information, such as force, acceleration, velocity, displacement. Variable names representing vectors are drawn with an underscore (x) or overarrow (~x). 11 12 COMO 102: Lecture 2, 2003 So, for example, Newton’s second law of motion can be written as: F~ = m ~a, where m, the mass, is a scalar (number) not a vector. For both row and column vectors we generally label the elements of a vector ~a with length n as a1 , a2 , . . ., an . This allows compact definitions of vector addition, subtraction, and scalar multiplication. Addition is defined only for equal length vectors, and: ~a + ~b = ~c ⇐⇒ ∀i ∈ {1, 2, . . . , n}, ai + bi = ci Thus, for example, [1, 2, 3] + [4, 5, 6] = [5, 7, 9]. Scalar multiplication is also done element-wise, so that α ~a = ~b ⇐⇒ ∀i ∈ {1, 2, . . . , n}, α ai = bi where α is a scalar. Scalar division can be defined in terms of scalar multiplication, because ~a/α = β ~a, where β = α1 . Subtraction can be defined as the addition of (the first vector) to (the second vector multiplied by the scalar -1), i.e. ~b − ~a ≡ ~b + (−1) ~a, thus ~b − ~a = ~c ⇐⇒ ∀i ∈ {1, 2, . . . , n}, bi − ai = ci 2.1.1 Examples of Basic Operations • [5, 5, 5] − [4, 6, 8] /2 = [3, 2, 1] • 2 [1, 2, 3] = [2, 4, 6] • 5 [1, 0, 0] − 2 [0, 1, 0] = [5, −2, 0] 2.1.2 Examples using MATLAB notation The main difference is that we must use ∗ to indicate multiplication. MATLAB does not recognise spaces as multiplication! >> [5, 5, 5] − [4, 6, 8] /2 >> 2 ∗ [1, 2, 3] >> 5 ∗ [1, 0, 0] − 2 ∗ [0, 1, 0] ans = [3, 2, 1] ans = [2, 4, 6] ans = [5, −2, 0] 13 COMO 102: Lecture 2, 2003 2.1.3 Linear Combinations A linear combination of objects is the sum of each object multiplied by a scalar. For example, which of the following are linear combinations of the objects 3, 2, 4 and ♥ ? • 3+♥ • 3 + ♥ + 4 − 43.82 ∗ 2 • 4∗2 • 42 + 3 • 2∗♥ 2 ∗ ♥ − 7.2 ∗ 2 3 In general, a linear combination of two vectors ~u and ~v can be written as: • 4+ α~u + β~v = α u1 u2 .. . um +β v1 v2 .. . vm α u1 + β v 1 α u2 + β v 2 = .. . α um + β v m . where α and β are real scalars. Linear Combinations of Vectors of Length 3 Consider two position1 vectors of length 3 (i.e. vectors in three dimensional space) that are not parallel. If we imagine all possible linear combinations of these vectors, what region in space do we cover? 2.1.4 Representing Vectors as Arrows A vector has three defining properties: 1. Magnitude 2. Direction 3. Sense All are clearly represented when it is drawn as an arrow: The magnitude of the vector is represented by how long the arrow is. The direction is represented by the orientation of the vector, and is independent of which end the arrow head is on. Two vectors with the same direction are said to be parallel, and have either the same sense or opposite sense. Sense is undefined for non-parallel vectors. 1 A position vector is a vector with it’s tail placed on the origin. It can be used to specify the position of a point in space. 14 COMO 102: Lecture 2, 2003 Different magitude. Same direction (parallel). Opposite sense. Same magnitude, different direction. 2.1.5 Same magnitude. Same direction (parallel). Same sense. The Dot Product The dot product is an operation between two vectors of the same dimension (i.e. with the same number of elements). It produces a scalar not a vector. We define it as: n X a i bi = a 1 b1 + a 2 b2 + . . . + a n bn ~a · ~b ≡ i=1 In MATLAB we can calculate the dot product of two vectors with the dot function: >> a=[1 2 3 4]; b=[2 2 2 2]; >> dot(a,b) ans = 20 The dot product can be used to find the angle between two vectors. In order to show this we use the cosine rule for a triangle and the distributive law of the dot product, resulting in2 : ~a · ~b = |~a||~b|cosθ where θ is the angle between the vectors. ~b θ ~a 2 me! The proof of this is excluded in the interests of time. If you’d like to see the proof come ask 15 COMO 102: Lecture 2, 2003 Dot product and Magnitudes The magnitude of a vector can be calculated using the dot product. To see this consider ~u · ~u: ~u · ~u = [u1 , u2 , . . . , un ] · [u1 , u2 , . . . , un ] = u21 + u22 + . . . + u2n Comparing this with our expression for a vectors magnitude from the first page we conclude that: ~u · ~u = |~u|2 Properties of the dot product The dot product is both commutative and distributive: ~a · ~b = ~b · ~a, and ~a · ~b + ~c = ~a · ~b + ~a · ~c. Scalars can be moved to the front of the expression from within a dot product, eg ~a · α ~b = α ~a · ~b . 2.1.6 Unit Vectors A unit vector is a vector with a magnitude of one (unit magnitude). For example the vector [ 1 0 0 ] is a unit vector, as is [ √12 0 √12 ]. We can find the unit vector corresponding to any given vector (except the zero vector) by scaling the vector by one over its magnitude (proof given on whiteboard). For example, the vector [ 1 0 1 ] has associated unit vector: √ 12 2.1.7 1 1 1 ∗[ 1 0 1 ]=[ √ 0 √ ] 2 2 +0 +1 2 2 Orthogonal Vectors Orthogonal vectors are vectors that are perpendicular, i.e. the angle between them is 90o . We can test for orthogonality using the dot product, as if ~a and ~b are orthogonal then ~a · ~b = 0. 2.1.8 Orthonormal Vectors Orthonormal vectors are unit vectors that are orthogonal. For example the vectors in three dimensions pointing along the coordinate axes, [1 0 0], [0 1 0] and [0 0 1] are clearly mutually orthonormal, as the dot product of any two is zero, and they are all unit vectors. 16 COMO 102: Lecture 2, 2003 2.2 Matrices A matrix is a rectangular array of real or complex numbers. The A matrix with m rows and n columns is written as A= a11 a21 .. . a12 a22 .. . . . . a1n . . . a2n .. . am1 am2 . . . amn Conventionally uppercase roman letters are used to designate an entire matrix. Elements are written as lowercase letters with two subscripts, the first giving the row number and the second the column number. A matrix with m rows and n columns is said to be an m by n matrix. 2.2.1 Matrices and Vectors in MATLAB In MATLAB the only difference between a vector and a matrix is the number of rows and columns each contains. Even scalars are treated as 1 by 1 matrices! This means that the routines which operate on matrices usually work on scalars and vectors also, although their behaviour may depend on the number of rows and columns. 2.2.2 Matrix Operations We give the component-wise rule for the following operations, as well as one example. Matrix Addition A + B = C ⇐⇒ ∀i, j, ai,j + bi,j = ci,j 1 2 3 3 3 3 4 5 6 1 2 3 + 4 4 4 = 5 6 7 1 2 3 5 5 5 6 7 8 Scalar Multiplication α B = C ⇐⇒ ∀i, j, α bi,j = ci,j 28 35 42 4 5 6 = 5 6 7 7∗ 35 42 49 42 49 56 6 7 8 17 COMO 102: Lecture 2, 2003 Matrix Subtraction A − B ≡ A + (−1 ∗ B) −2 −1 0 3 3 3 1 2 3 1 2 3 − 4 4 4 = −3 −2 −1 −4 −3 −2 5 5 5 1 2 3 Matrix Transpose The matrix transpose is written as AT mathematically, but MATLAB uses the ’ symbol, e.g. A’. AT = B ⇐⇒ ∀i, j, ai,j = bj,i T 1 2 3 1 4 7 4 5 6 = 2 5 8 7 8 9 3 6 9 Matrix Product The matrix product is not as simple as the previous operations. In order to calculate A ∗ B the inner matrix dimensions of A and B must agree. Suppose A is an m by r matrix, and B is an r by n matrix. A= a11 a21 .. . a12 a22 .. . . . . a1r . . . a2r .. . am1 am2 . . . amr B= If A ∗ B = C then C is an m by n matrix, with elements such that ci,j = r X k=1 Example: " 1 2 3 4 5 6 # ai,k ∗ bk,j b11 b12 . . . b1n b21 b22 . . . b2n .. .. .. . . . br1 br2 . . . brn " # 2 1 32 9 ∗ 3 4 = 71 24 8 0 Lecture 3 : Graphics in MATLAB Chapter 3 Before we learn about graphics and plotting in MATLAB we need to become familiar with the array operators in MATLAB. Graphics in MATLAB 3.1 Vectorisation and Array Operators All of the built-in functions in MATLAB are able to operate on vectors, where the operation is applied to each element in the vector. For example, if we ask for the square root of a vector, an operation that is traditionally undefined, MATLAB complies by taking the square root of each element within the vector. The results are, of course, outputted as a vector: >> sqrt([4 9 16]) ans = 2 3 4 Such ‘vectorisation’ allows us to perform operations with compact syntax, as we don’t have to write a loop that takes the square root of each element one by one. Of still greater importance is the improved computational efficiency - modern processors are able to take advantage of vectorisation to compute many operations simultaneously! To support vectorisation MATLAB defines new arithmetic symbols called array operators which perform element-by-element operations on matrices or vectors. Often these operations have no equivalent in formal linear algebra - they are defined in MATLAB not for their mathematical merit but for convenience and speed of the resulting code. Array operations view the matrices as data structures, not as mathematical entities! In summary, vectorisation: • Allows compact notation. • Uses features of modern processors to increase the speed of your code. • Does not always have mathematical equivalents. 18 19 COMO 102: Lecture 3, 2003 3.1.1 Array Operators in MATLAB The array operators used for vectorisation in MATLAB are a combination of a full stop and one of the conventional operators (e.g. .* ./ .^). Remember however that all the inbuilt scalar functions work element-by-element on vectors also! Multiplication Matrix multiplication: 30 24 18 9 8 7 1 2 3 69 54 4 5 6 ∗ 6 5 4 = 84 138 114 90 3 2 1 7 8 9 Vectorised multiplication: 9 16 21 9 8 7 1 2 3 4 5 6 . ∗ 6 5 4 = 24 25 24 21 16 9 3 2 1 7 8 9 In MATLAB we use the following. Note I have used the reshape function to reshape easily created row vectors into 3x3 matrices. >> A=reshape([1:9],3,3)’ A = 1 2 3 4 5 6 7 8 9 >> B=reshape([9:-1:1],3,3)’ B = 9 8 7 6 5 4 3 2 1 >> A.*B ans = 9 24 21 16 25 16 21 24 9 20 COMO 102: Lecture 3, 2003 Division Matrix division is undefined. Vectorised division is as you would expect: 9 8 7 1 2 3 4 5 6 ./ 6 5 4 = 3 2 1 7 8 9 2 8 5 5 8 2 1 9 4 6 7 3 3 7 6 4 9 1 In MATLAB: >> A./B ans = 0.1111 0.6667 2.3333 3.2 0.2500 1.0000 4.0000 0.4286 1.5000 9.0000 Plotting in Two Dimensions MATLAB generates plots based on arrays or matrices of numerical values. If we wish to plot a known function we need to create these arrays for MATLAB, as it is unable to handle symbolic notation1 . 3.2.1 The Plot Command In it’s most basic form the Plot command takes two vectors of equal length (i.e. with the same number of components), one vector representing the x values, and one representing the y values. For example: >> x = [ 0 1 2 3 4 5]; >> plot(x,y) y = [ 5 3 6 8 7 4]; 8 7.5 7 6.5 6 5.5 5 4.5 4 3.5 3 1 A. 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5 MATLAB does have a primitive symbolic manipulation toolbox, but it is not available in LAB 21 COMO 102: Lecture 3, 2003 The graph appears in a new window. By default any further calls to plot will replace that graph, if we wish to keep it we open a second window by entering the command figure. Plot allows us to specify the line style, symbols to draw at the points, and colours. We can plot multiple sets of data, put on axes labels, place a grid in the plot, insert plot titles, and so forth. For example, consider the following: >> >> >> >> >> x=linspace(0,2*pi); y=sin(x); plot(x,y); axis([0 2*pi -1.5 1.5]) title(’sin(x)’) sin(x) 1.5 1 0.5 0 −0.5 −1 −1.5 1 2 3 4 5 6 x=linspace(0,2*pi,20); y1=sin(x); y2=sin(x)+rand(size(x))-0.5; plot(x,y1,’ro-’,x,y2,’b+’); axis([0 2*pi -1.8 1.8]) xlabel(’angle, x’); ylabel(’function value’); legend(’sin(x)’,’randomized sine(x)’); title(’Plot of sin(x) with and without noise.’); Plot of sin(x) with and without noise. sin(x) randomized sine(x) 1.5 1 0.5 function value >> >> >> >> >> >> >> >> >> 0 0 −0.5 −1 −1.5 0 1 2 3 angle, x 4 5 6 22 COMO 102: Lecture 3, 2003 Note that the line >> y2=sin(x)+rand(size(x))-0.5; uses the command rand to create a vector that is the same size as x, with random numbers between 0 and 1 as its elements. An example of the use of rand is: >> rand(1,5) ans = 0.6602 0.3420 0.2897 0.3412 0.5341 We also subtract 0.5 from the resultant vector of random numbers. We can subtract scalars from vectors, because MATLAB uses array operations to subtract the scalar from each element of the vector, e.g.: >> [1 2 3] - 1 ans = 0 1 2 Subtracting 0.5 effectively shifts the random numbers to be centered on the origin, so rand(size(x))-0.5 represents a vector of the same size as x that contains random numbers in the range [−0.5, 0.5]. We add this vector of random numbers between −0.5 and 0.5 to sin(x), which uses the vectorised sin command to calculate the sine of every element in the vector x. The result is equivalent to: >> y2 = sin(x) + rand(size(x)) - 0.5*ones(size(x)) ; Question: why does the following produce significantly different results? >> y2 = sin(x) + rand(1) - 0.5 ; 23 COMO 102: Lecture 3, 2003 3.2.2 MATLAB’s Help Files PLOT Linear plot. PLOT(X,Y) plots vector Y versus vector X. If X or Y is a matrix, then the vector is plotted versus the rows or columns of the matrix, whichever line up. If X is a scalar and Y is a vector, length(Y) disconnected points are plotted. PLOT(Y) plots the columns of Y versus their index. If Y is complex, PLOT(Y) is equivalent to PLOT(real(Y),imag(Y)). In all other uses of PLOT, the imaginary part is ignored. Various line types, plot symbols and colors may be obtained with PLOT(X,Y,S) where S is a character string made from one element from any or all the following 3 columns: y m c r g b w k yellow magenta cyan red green blue white black . o x + * s d v ^ < > p h point circle x-mark plus star square diamond triangle (down) triangle (up) triangle (left) triangle (right) pentagram hexagram : -. -- solid dotted dashdot dashed For example, PLOT(X,Y,’c+:’) plots a cyan dotted line with a plus at each data point; PLOT(X,Y,’bd’) plots blue diamond at each data point but does not draw any line. PLOT(X1,Y1,S1,X2,Y2,S2,X3,Y3,S3,...) combines the plots defined by the (X,Y,S) triples, where the X’s and Y’s are vectors or matrices and the S’s are strings. For example, PLOT(X,Y,’y-’,X,Y,’go’) plots the data twice, with a solid yellow line interpolating green circles at the data points. The PLOT command, if no color is specified, makes automatic use of the colors specified by the axes ColorOrder property. COMO 102: Lecture 3, 2003 24 RESHAPE Change size. RESHAPE(X,M,N) returns the M-by-N matrix whose elements are taken columnwise from X. An error results if X does not have M*N elements. RESHAPE(X,M,N,P,...) returns an N-D array with the same elements as X but reshaped to have the size M-by-N-by-P-by-... M*N*P*... must be the same as PROD(SIZE(X)). RESHAPE(X,[M N P ...]) is the same thing. In general, RESHAPE(X,SIZ) returns an N-D array with the same elements as X but reshaped to the size SIZ. PROD(SIZ) must be the same as PROD(SIZE(X)). See also SQUEEZE, SHIFTDIM, COLON. AXIS Control axis scaling and appearance. AXIS([XMIN XMAX YMIN YMAX]) sets scaling for the x- and y-axes on the current plot. AXIS AUTO returns the axis scaling to its default, automatic mode where, for each dimension, ’nice’ limits are chosen based on the extents of all line, surface, patch, and image children. AXIS TIGHT sets the axis limits to the range of the data. AXIS EQUAL sets the aspect ratio so that equal tick mark increments on the x-,y- and z-axis are equal in size. This makes SPHERE(25) look like a sphere, instead of an ellipsoid. AXIS IMAGE is the same as AXIS EQUAL except that the plot box fits tightly around the data. AXIS SQUARE makes the current axis box square in size. AXIS NORMAL restores the current axis box to full size and removes any restrictions on the scaling of the units. This undoes the effects of AXIS SQUARE and AXIS EQUAL. AXIS OFF turns off all axis labeling, tick marks and background. AXIS ON turns axis labeling, tick marks and background back on. Lecture 4 : MATLAB’s M-files Chapter 4 4.1 Script m-files MATLAB’s M-files MATLAB allows the user to write script files (‘m-files’) to simplify the implementation of complicated algorithms. Example: %.This.is.the.script.file.‘trigplot’. %.It.draws.a.graph.of.sin(t),.cos(t).and.sin(t)*cos(t). t.=.linspace(0,2*pi); y1=sin(t); y2=cos(t); y3=y1.*y2; plot(t,y1,’-’,t,y2,’.’,t,y3,’--’); axis([0.2*pi.-1.5.1.5]); legend(’sin(\theta)’,’cos(\theta)’,’sin(\theta)*cos(\theta)’); 1.5 sin(θ) cos(θ) sin(θ)*cos(θ) 1 0.5 0 −0.5 −1 −1.5 0 1 2 3 4 5 6 The script file is executed by entering the name of the file, and it evaluates the lines of the file one by one as if you typed them in the command window. 25 26 COMO 102: Lecture 4, 2003 4.2 Function m-files One limitation of script files is their inability to take parameters that modify their behaviour. To do this MATLAB allows specification of function m-files. E.g. function.[.cms.].=.inchconv(.inches.) %INCHCONV.Converts.a.measurement.in.inches.to.centimeters. %..Based.on.1.inch.=.2.54.centimeters. cms.=.2.54.*.inches; • The first line (the function declaration) defines the input and output values and specifies the function name. The name of the function (inchconv here) should ALWAYS match the filename! • The comment lines, those starting with the % symbol, describe the function. Comments immediately following the function declaration appear when help functionname is called (help inchconv in this case). • The remaining lines perform operations on the input matrices. We have one input matrix here - inches, and one output matrix - cms. • Examples of calling this routine are: >> inchconv(1) ans = 2.5400 >> inchconv(253.1234) ans = 642.9334 >> inchconv([ 1 2 3; 4 5 6]) ans = 2.5400 5.0800 7.6200 10.1600 12.7000 15.2400 >> help inchconv INCHCONV Converts a measurement in inches to centimeters. Based on 1 inch = 2.54 centimeters. We could easily modify this routine to convert to other units1 , e.g. : • 1 inch = 100 caliber (gun barrel caliber) • 1 inch = 0.00001716 roman miles • 1 inch = 8.23E-19 parsecs (where E-19 means ∗10−19 ) • 1 inch = 0.11111111 span (cloth span) • 1 inch = 0.00023148 skein • 1 inch = 1.14 finger 1 Courtesy of www.convertit.com. 27 COMO 102: Lecture 4, 2003 4.2.1 plotData - A More Complicated Example function.plotData(fname) %.plotData...Plot.(x,y).data.from.columns.of.an.external.file % %.Input:.....fname.=.(string).name,.including.extension,.of.the. %....................file.containing.data.to.be.plotted data.=.load(fname);....%..load.contents.of.file.into.data.matrix.. x.=.data(:,1);.........%..x.and.y.are.in.first.two.columns.of.data y.=.data(:,2); plot(x,y,’o’); This loads data from the given file of the form: first_x_value second_x_value third_x_value ..etc.. first_y_value second_y_value third_y_value ..etc.. An example usage is: >> plotData(’xy.dat’); which yields: 6 4 2 0 −2 −4 −6 4.2.2 0 1 2 3 4 5 6 7 8 Use of Subfunctions MATLAB version 6 allows us to define subfunctions within one m-file (although Octave, a free equivalent of MATLAB, does not). The subfunctions are hidden to everything outside the m-file, and cannot be called from the command window. An example follows, which calculates the area and perimeter of an n-sided polygon with ‘radius’ r. 28 COMO 102: Lecture 4, 2003 s r θ a = 21 nr2 sin p = 2nr sin π n s = 2r sin π n 2π n n=6 function.[a,p].=.polyGeom(n,r) %.polyGeom..Compute.area.and.perimeter.of.a.regular.polygon % %.Input:..r.=.radius.of.smallest.enclosing.circle %.........n.=.number.of.sides.of.the.polygon % %.Output:.a.=.total.area.of.the.polygon %.........p.=.total.perimeter.of.the.polygon a.=.area(r,n); p.=.perimeter(r,n); %.============.subfunction."area" function.a.=.area(r,n) %.Computes.the.area.of.an.n-sided.polygon.of.radius.r a.=.0.5*n*r^2*sin(2*pi/n); %.============.subfunction."perimeter" function.p.=.perimeter(r,n) %.Computes.the.perimeter.of.an.n-sided.polygon.of.radius.r p.=.n*2*r*sin(pi/n); Examples using polyGeom: >> [a,p]=polyGeom(4,sqrt(2)) a = 4.0000 p = 8 >> [a,p]=polyGeom(6,1) a = 2.5981 p = 6.0000 >> [a,p]=polyGeom(6,2) a = 10.3923 p = 12.0000 COMO 102: Lecture 4, 2003 4.3 Flow Control: If statements and for loops An example illustrating if statements and for loops follows. function.[.C.].=.addmtx(.A,.B) %ADDMTX.-.adds.the.matrix.A.to.the.matrix.B,.returning.C. %..A.slow.and.inefficient.way.to.add.matrices.-.uses.loops. if.(any(size(A)~=size(B)))..%.Do.both.dimensions.agree?.If.not... ....disp(’The.matrices.are.not.of.the.same.size!’); ....return; end; [m,n]=size(A);...%.which.is.the.same.as.size(B). C.=.zeros(m,n);..%.MATLAB.can.enlarge.matrices.as.we.add.elements .................%.but.its.very.slow,.so.we.initialise.the.matrix. for.i=1:m ....for.j=1:n ........C(i,j)=A(i,j)+B(i,j); ....end end %.we’re.done!.C.will.be.returned.when.the.function.is.called. We test the routine as follows: >> A=reshape([1:9],3,3)’; >> B=reshape([-5:3],3,3)’; >> addmtx(A,B) ans = -4 2 8 -2 4 10 0 6 12 -2 4 10 0 6 12 >> A+B ans = -4 2 8 29 COMO 102: Lecture 4, 2003 4.3.1 30 Relational Operators MATLAB uses 0 to represent ‘false’, and any non-zero number (often 1) to represent ‘true’. Examples of relational operators are: >> 2<4 % 2 is less than four... ans = 1 % true. >> %------------------------------------------------->> 2>4 % 2 is greater than four... ans = 0 % false. >> %------------------------------------------------->> 2==4 % 2 is equal to four... ans = 0 >> %------------------------------------------------->> 2~=4 % 2 is NOT (~) equal to four... ans = 1 >> %------------------------------------------------->> 2<4 & 2>4 % 2<4 AND (&) 2>4 ans = 0 >> %------------------------------------------------->> 2<4 | 2>4 % 2<4 OR (|) 2>4 ans = 1 >> %------------------------------------------------->> 2<4 & ~(2>4) ans = 1 31 COMO 102: Lecture 4, 2003 These operators can of course be used on vectors and matrices: >> A=[1:9] A = 1 2 3 4 5 6 7 8 9 1 1 1 0 0 0 0 0 -1 1 3 5 7 9 11 13 0 0 0 0 1 1 1 1 >> A<5 ans = 1 >> B=[-3:2:13] B = -3 >> A<B ans = 0 We can then use the built-in logical functions such as: • all(X) - true if all elements of X are non-zero (true). • any(X) - true if any elements of X are non-zero (true). • isreal(X) - true if X only has real (non-complex) elements. For example: >> all(A<B) % is every element in A less than the corresponding element in B? ans = 0 >> any(A<B) % are any elements in A less than the corresponding element in B? ans = 1 32 COMO 102: Lecture 4, 2003 4.3.2 The if statement in MATLAB The if command takes the following form: if (expression ) ... elseif (expression ) ... else ... end for example: if.(i<0) ........disp(’i.is.less.than.zero.’); ........%.can.have.more.commands.here... elseif.(i==0) ........disp(’i.is.equal.to.zero.’); else ........disp(’i.is.greater.than.zero.’); end 4.3.3 Using for loops in MATLAB The basic format of a for loop is: for variable = vector ... end The for loop executes the block of statements once for each element of the vector in turn, with the variable assigned to the current element. octave:1> y=0; octave:2> for x=[1 2 9 10]; y=y+x; end; octave:3> y y = 22 More normally it is used with the colon notation, e.g. %.a.script.m-file.showing.for.loops. y.=.zeros(1,12); y(1)=1;.y(2)=1; for.x=3:12 .....y(x).=.y(x-1).+.y(x-2); end disp(y); octave:1> addition 1 1 2 3 5 8 13 21 34 55 89 144 Lecture 5 : Differential Equations: Chapter 5 Euler’s Method Differential Equations: Euler’s Method 5.1 Derivatives Covered on the whiteboard: 1. Derivatives as limits. 2. Derivatives and integrals. 3. Differential equations. 5.2 Introduction: ODEs In this lecture we consider first order ordinary differential equations (ODEs) of the form dy = f (t, y) (5.1) dt where f (t, y) is a function of the independent variable t and the dependent variable an ordinary derivative as y is a function of t alone1 . Equation (7.1) y. We call dy dt gives an expression f (t, y) representing the rate of change of the dependent variable y with respect to the independent variable t. Geometrically we can think of this as an equation specifying the slope of the graph at each point in terms of it’s position, with the position to be determined from this information. Equation (5.1) does not completely specify y(t). In fact, it has an infinite number of solutions. To specify a unique y(t) we must also have an initial condition, such as y(t0 ) = y0 . The initial condition is usually written together with the differential equation to form an initial value problem, i.e. dy = f (t, y), dt y(t0 ) = y0 . (5.2) We may also specify a range of t over which the equation is to be solved (e.g. t0 ≤ t ≤ tN ). Note that we use t as our independent variable, implying that 1 Partial differential equations have functions of two or more variables, and involve ‘partial’ derivatives (derivatives evaluated by holding all but one variable fixed). 33 34 COMO 102: Lecture 5, 2003 our problem is time dependent. The independent variable could just as easily be x (implying a position dependent problem), or represent some other quantity entirely. In the case of x as the independent variable, the ‘initial’ value can is placed on a boundary of the domain, often at x = 0. Equation 5.2 represents a first order ODE because the highest derivative is of first order. Ordinary differential equations exist with second, third and higher order derivatives. We can also have coupled systems of differential equations, i.e. several equations and initial conditions of the type shown in Equation (5.2), each with it’s own dependent variable, which is dependent on the other dependent variables as well as the independent variable. For example, a simple system of coupled ODEs is given by: dy1 = αy1 + βy2 + g1 (t), dt dy2 = γy1 + δy2 + g2 (t), dt y1 (t0 ) = y1,0 , y2 (t0 ) = y2,0 . Here α, β, γ and δ are given coefficients (i.e. we know what they are when we begin to solve the problem), and g1 (t) and g2 (t) are known functions of t. The numerical methods we will discuss are primarily designed for solving firstorder ODEs. Higher order ODEs can be solved numerically by first converting them into a mathematically equivalent system of coupled, first-order ODEs2 . 5.2.1 Example: Newton’s Law of Motion as an ODE Recall that the one-dimensional motion of an object under the action of an applied force is governed by F = ma where F is the magnitude of the force, m the mass of the object, and a its acceleration. Consider the case where the force F is varied with time, so that F = F (t). We can formulate the problem with either displacement or velocity as the dependent variable. Displacement will lead to a second order ODE, and velocity a first order ODE. Velocity as the Dependent Variable Consider velocity as the dependent variable, where a = dv dt and so F (t) dv = . dt m If the function F (t) is known, as is the velocity at some time t0 , then this equation can be integrated to find v(t). More interesting problems have F dependent on v, t and other parameters rather than just on t. 2 This is done by introducing new variables representing the derivatives, thus producing the system of coupled differential equations. 35 COMO 102: Lecture 5, 2003 5.2.2 Example: Newton’s Law of Cooling Consider the situation shown in the following figure. T∞ Ts m, c An object of surface temperature Ts , mass m and ‘specific heat’ c is immersed in fluid which flows over the object cooling it. The fluid temperature away from the object is labelled T∞ , and Newton postulated that the rate at which heat is exchanged between object and fluid is proportional to the difference between Ts and T∞ , i.e. Q = HA(Ts − T∞ ), where Q is the instantaneous heat transfer rate, H is the ‘heat transfer coefficient’, and A is the surface area of the object. This equation can be substituted into an equation for the conservation of energy of the system. If we assume that heat travels quickly through the object, so that the instantaneous temperature of the object T is assumed to be equal everywhere in the object to the surface temperature Ts , then we have mc dT = −Q = −HA(T − T∞ ), dt which gives us dT HA =− (T − T∞ ) . dt mc This equation is now in the form of Equation (5.1). It is linear, so may be solved directly, but is also suitable for numerical analysis. 36 COMO 102: Lecture 5, 2003 5.3 Strategy for the Numerical Methods Our goal is to find a numerical approximation to y(t) given an initial value problem of the form of Equation (5.2). We can write y(t) = y0 + Z t t0 f (t̃, y) dt̃, so that the solution to Equation (5.2) can be thought of as the evaluation of an integral. An ODE of the form dy = f (t) can be integrated with standard numerical dt integration techniques, such as Gaussian Quadrature, Newton-Cotes Rules, etc. However if f = f (t, y) then more advanced methods are needed, as will be discussed here. The general idea is to start at the given initial value, evaluate the integral over a short time step, move to a new estimated position, evaluate the integral there, move to a new estimated position, and so on. Thus the numerical solution to Equation (5.2) is only obtained at a finite number of t values. The numerical solution is therefore said to be discrete as opposed to the exact solution y(t) which is continuous. Let the sequence of discrete time values, that we will produce an approximate numerical solution to y(t) at, be given by tj = t0 + jh, j = 0, 1, 2, . . . , N, where the spacing parameter h is called the stepsize. The basic techniques use a constant value of h for the entire process, while more advanced methods adapt the value of h to the local behaviour of the solution, using smaller stepsize where the value of the function changes rapidly (“adaptive” methods). The numerical solution to Equation (5.2) is obtained by producing a series of yj values at the corresponding tj , where yj ≈ y(tj ). Recall that y(tj ) is the exact solution evaluated at t = tj . We can write the error in the numerical solution at tj as ej = yj − y(tj ). 37 COMO 102: Lecture 5, 2003 5.4 Euler’s Method Euler’s method is based on a “Taylor Series” expansion of y(t). A Taylor Series is a way of writing a function as an infinite polynomial, and is useful in producing approximations to the function using a simple expression. The Taylor Series expansion of y(t) about t = t0 is: y(t) = y(t0 ) + (t − t0 )y 0 (t0 ) + (t − t0 )2 00 y (t0 ) + . . . , 2 where y 0 is the first derivative of y, and y 00 is the second derivative. If t and t0 are very close together then |t − t0 | << 1, and the higher order terms are negligible, giving us y(t) ≈ y(t0 ) + (t − t0 ) y 0 (t0 ) = y(t0 ) + (t − t0 ) f (t0 , y0 ), since y 0 (t0 ) = f (t0 , y0 ) by Equation (5.1). We can use this to calculate y1 ≈ y(t1 ) in terms of y0 : y1 = y0 + h f (t0 , y0 ), where h = (t1 − t0 ). The same procedure can produce y2 : y2 = y1 + h f (t1 , y1 ), and so forth. 5.4.1 Euler’s Method is a series of Straight Line Approximations It may not surprise you to learn that Euler’s method is just a series of straight line approximations to the to the exact solution. (This is demonstrated in the lecture on the white-board). Lecture Chapter6 :6Programming in MATLAB: The Asteroid Problem Programming in MATLAB: The Asteroid Problem 6.1 An Asteroid under Earth’s Gravity A group of astronomers have just returned from their lunch break, and notice a previously unseen asteroid hurtling towards the Earth. They telephone you, and ask if the asteroid will impact the planet or narrowly miss. During the conversation you learn that: • The radius of the Earth is 6378 km, and its mass is 5.972 · 1024 kg. • The approach is planar (i.e. the path of the asteroid lies in a plane), so the problem can be modelled in two dimensions. • Choosing a coordinate system so that the Earth is positioned at the origin, the asteroid is currently at x = 20, 000km and y = 20, 000km. • The asteroids velocity is currently parallel to the x-axis, and its speed is between 3 and 5 km s−1 . • The magnitude of the gravitational force between two bodies is given by |F~ | = GM m , r2 where G is the universal gravitational constant (G = 6.672 · 10−11 Nm2 kg−2 ), M is the mass of the first body (the Earth), m the mass of the second body (the asteroid), and r the distance between them. Describing the path of the asteroid analytically is difficult, so you approximate its path using Euler’s method. The independent variable is time (t), and the initial position of the asteroid is at t0 = 0. You write the step size as h = ∆t , so that Euler’s method will give the asteroid’s position at times t = 0, ∆t , 2∆t , 3∆t , . . .. The calculation will continue until either the asteroid has impacted the Earth or escaped the solar system. 38 39 COMO 102: Lecture 6, 2003 Asteroid at time t = 2∆t (third position). Asteroid at time t = ∆t (second position). Asteroid at time t = 0. Asteroid at time t = 0. Asteroid at time t = n∆t . We calculate each position in turn. The asteroid’s position and velocity at t = ∆t is calculated from its position, velocity and acceleration at t = 0. Then the asteroid’s position and velocity at t = 2∆t can be calculated from its position, velocity and acceleration (which is easily determined) at t = ∆t . This process continues until we reach t = n∆t for some large n, and we have then found the asteroids path. 6.2 One Step of the Asteroids Motion At time t = i · ∆t , let the vector ~xi be the asteroids position, ~vi be its velocity and ~ai be its acceleration. Choose our coordinate system so that the Earth is at the origin. Then we have the following situation. ~vi+1 Asteroid at time (i + 1) · ∆t ~vi ~ai+1 Asteroid at time i · ∆t ~ai ~xi+1 ~xi The Earth 40 COMO 102: Lecture 6, 2003 6.2.1 Calculating the Acceleration Vector The acceleration vector can be written in terms of its magnitude multiplied by a unit direction vector (giving the direction and sense of the acceleration vector), i.e. ~a ∗ ~a = |~a| (6.1) |{z} |~a| |{z} magnitude direction The magnitude of the acceleration vector can be found using Newton’s Law: F~ = m~a ⇒ |F~ | = m |~a| (6.2) And the magnitude of the gravitational force, |F~ |, for two bodies (with masses M and m) a distance r apart is: GM m |F~ | = . (6.3) r2 Substituting Eq. 6.3 into Eq. 6.2 we have the following (where r = |xi | because the centre of the Earth is at the origin). GM m GM GM = m |~ a | ⇒ |~ a | = = . (6.4) r2 r2 |~xi |2 Thus the gravitational acceleration of the asteroid due to the Earth is independent of the mass of the asteroid! The acceleration vectors always points towards the centre of the Earth (why is this so?). Then referring back to the diagram we see that ~ai has the same direction and sense as −~xi . In terms of unit vectors we have ~xi ~ai =− . (6.5) |~ai | |~xi | This tells us the orientation of the acceleration vector (the direction of the acceleration). Substituting Eq. 6.4 and Eq. 6.5 into Eq. 6.1 yields ~ai = Thus GM |~xi |2 ! ~ai = −~xi 6.2.2 ! ~xi − . |~xi | GM |~xi |3 Calculating the New Position and Velocity Under constant acceleration the new velocity is easily found: d~v = ~a , so from Euler’s method, ~vi+1 = ~vi + ∆t ~ai dt Similarly the velocity will be nearly constant over the small time interval ∆t , and so Euler’s method gives ~xi+1 = ~xi + ∆t ~vi . This gives us iterative equations for the approximated position and velocity: ~vi+1 = ~vi + ~ai ∆t , ~xi+1 = ~xi + ~vi ∆t 41 COMO 102: Lecture 6, 2003 6.2.3 An Algorithm to Solve the Asteroid Problem 1. Choose the number of steps n and the time interval per step ∆t . 2. Initialize matrices X and V (both with n + 1 rows and 2 columns). 3. Choose a starting position vector ~x0 and velocity vector ~v0 for the asteroid. 4. Set the first row of X to be ~x0 , and the first row of V to be ~v0 . 5. Set i = 0. 6. While i < n repeatedly do the following: (a) Calculate ~ai using ~ai = −~xi (b) Calculate ~vi+1 and ~xi+1 using ~vi+1 = ~vi + ~ai ∆t , GM |~xi |3 ~xi+1 = ~xi + ~vi ∆t (c) Store ~xi+1 in row i + 2 of X and ~vi+1 in row i + 2 of V. (d) If |~xi+1 | < 6378km display ”Impact with Earth!” message, impact velocity and time, then terminate. (e) Let i = i + 1. 7. Output or graph the positions stored in X and the velocities stored in V . For example, with an initial velocity of 5 km · s−1 , we get: 7 x 10 2 1 y position (metres) 0 −1 −2 −3 −4 −5 −4 −3 −2 −1 0 1 x position (metres) 2 3 4 7 x 10 42 COMO 102: Lecture 6, 2003 6.2.4 Errors Clearly there are small errors associated with using Euler’s method, which amounts to assuming that the derivatives of the dependent variables (position and velocity) are constant over the small time steps. These errors build on one another, slowly pushing the calculated path further and further away from the true solution. The graph below shows two paths, one more accurate than the other. 7 x 10 2 1 y position (metres) 0 −1 −2 −3 −4 −5 −4 −3 −2 −1 0 1 x position (metres) 2 3 4 7 x 10 • Which path is the more accurate of the two? • How can we improve the accuracy of a path that is calculated using the above approach? • Can you think of a scheme which would iterate, at each step finding a successively more accurate approximated path, and stop when we have found a path with sufficient accuracy? 43 COMO 102: Lecture 6, 2003 6.3 Multiple Planets Planet 1 Planet 2 F~1 F~total F~2 F~3 = F~1 + F~2 + F~3 Planet 3 When considering multiple planets the total force on the asteroid due to the planets is just the sum of the individual force vectors. Similarly the total acceleration is just the sum of the acceleration vector for each planet! Planet ~a p ~ ~x Asteroid If we consider a planet at position p~ with mass M and the asteroid at position ~x then the acceleration of the asteroid due to that planet is: ~a = (~ p − ~x) GM r3 where r = |~ p − ~x|. 44 COMO 102: Lecture 6, 2003 6.3.1 Three Planets For example, the m-file “threeplanets.m” on COMO produces the following image: >> threeplanets COLLISION WITH PLANET! Impact velocity is 10902.360138 m/s. Time is t=98150.000000 seconds. 7 Trajectory of Asteroid x 10 2 venus 1 earth 0 metres −1 mars −2 −3 −4 −5 −6 −6 −4 −2 0 metres 2 4 7 x 10 Lecture 7 : Various Topics Chapter 7 7.1 Revision Various Topics In the last lecture we considered first order ordinary differential equations (ODEs) of the form dy = f (t, y) (7.1) dt where f (t, y) is a function of the independent variable t and the dependent variable y. The differential equation is usually written together with an initial condition to form an initial value problem, i.e. dy = f (t, y), dt 7.1.1 y(t0 ) = y0 . (7.2) Strategy for the Numerical Methods Our goal is to find a numerical approximation to y(t) given an initial value problem of the form of Equation (7.2). The general idea is to start at the given initial value, then at each iteration we calculate the slope at the current value, and move a short ways in a direction determined by the slope to arrive at the next value. The numerical solution to Equation (7.2) is obtained only at a finite number of t values (i.e. the numerical solution discrete as opposed to the continuous exact solution y(t)). 45 46 COMO 102: Lecture 7, 2003 7.1.2 Euler’s Method As we have seen, Euler’s method is a simple way of numerically solving Equation (7.2). At each iteration, Euler’s method uses the tangent line to the curve to calculate the next value. y Exact Solution Approximate Solution t We saw in the last exercise that Euler’s method produces more accurate approximations to the true solution y(t) as we decrease the ‘step size’ h. 1 h=0.2 h=0.1 h=0.05 Exact 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0 0.2 0.4 0.6 0.8 1 1.2 1.4 1.6 1.8 2 However, the smaller we choose h to be, the greater the number of calculated points, and the longer it takes for us to generate our numerical solution. Higher accuracy methods, which require fewer calculations to produce a solution with a given accuracy, generally choose the next point at each iteration in a more sophisticated way. Can you think of a modification to Euler’s method that would improve accuracy? COMO 102: Lecture 7, 2003 7.2 47 ode23 and ode45 Unsurprisingly MATLAB contains sophisticated, fast and accurate ODE IVP solvers. There are many ways to improve upon Euler’s method; adaptive choices of step size (using smaller steps where the function is changing rapidly), higher order approximations, and switching between combinations of methods as necessary all provide significant performance and accuracy gains. MATLAB has entire toolboxes devoted to the solutions of differential equations, and can be used to solve a wide variety of problems. The basic workhorse routines are ode23 and ode45, which can be used as direct replacements (with one minor change) for your odeEuler routine from the last exercise. 7.2.1 ode23 ODE23 Solve non-stiff differential equations, low order method. [T,Y] = ODE23(ODEFUN,TSPAN,Y0) with TSPAN = [T0 TFINAL] integrates the system of differential equations y’ = f(t,y) from time T0 to TFINAL with initial conditions Y0. Function ODEFUN(T,Y) must return a column vector corresponding to f(t,y). Each row in the solution array Y corresponds to a time returned in the column vector T. To obtain solutions at specific times T0,T1,...,TFINAL (all increasing or all decreasing), use TSPAN = [T0 T1 ... TFINAL]. To solve the question in the exercise, where y 0 (t) = t − 2y, we could use the following “ODEFUN” function: function dydt = myodefun(t,y) dydt = t - 2*y; Lets assume we are interested in 0 ≤ t ≤ 10, and at t = 0 we have the initial condition that y = 1. We can then call ode23 and plot the result with the following code: [t,y] = ode23( ’myodefun’, [0, 10], 1 ); plot(t,y); Note that we do not need to specify a step size, h. This is because ode23 chooses appropriate step sizes as necessary. We can use the TSPAN = [T0 T1 ... TFINAL] form to force it to use a given step size; for example we could force h = 0.2 by replacing the [0, 10] in the above code with 0:0.2:10. 7.2.2 ODE45 ode45 Solve non-stiff differential equations, medium order method. COMO 102: Lecture 7, 2003 7.3 48 File Input and Output (“I/O”) in MATLAB The ability to save and restore your calculated or entered data is a crucial part of scientific programming. This also allows you to import and export data, enabling you to use MATLAB together with other software. 7.3.1 ASCII and MAT files MATLAB has simple routines for loading and saving “MAT” binary files (MATLABonly format) and “ASCII” text files (readable and writable by many programs). MAT loading and saving To save a variable myvar to the file filename.mat you can issue the command: save filename myvar If the variable name is omitted (i.e. you just type save filename) then all the current workspace variables are saved together in the one file! You can save multiple variables of your choosing in one file by issuing save filename variable1 variable2 ... Once you have saved myvar in the file filename.mat you can load myvar in another MATLAB session by entering: load filename There is no need to specify the variable name (myvar) - MATLAB will load all the variables stored in filename.mat automatically. ASCII loading and saving To save an ASCII file we use save as above, but with the addition of -ascii at the end of the line, and we save it as a filename.txt. For example: >> >> >> >> x = 0:5; y = 5*x; XY = [x’ y’]; save xyvals.txt XY -ascii type xyvals.txt 0.0000000e+000 1.0000000e+000 2.0000000e+000 3.0000000e+000 4.0000000e+000 5.0000000e+000 0.0000000e+000 5.0000000e+000 1.0000000e+001 1.5000000e+001 2.0000000e+001 2.5000000e+001 Note that the numbers are saved in scientific format, e.g. 15 is written as 1.5000000e+001. To load this file we use the ‘load’ command: >> load -ascii xyvals.txt The results are stored in a new variable of the same name as the file (xyvals here). 49 COMO 102: Lecture 7, 2003 7.4 Machine Precision Consider the following code fragment: epsilon.=.1; while.(..1.+.epsilon..~=..1..). ....epsilon.=.epsilon./.2; end disp(epsilon); Imagine a perfectly accurate computer. On such a machine this routine would run forever, with getting smaller and smaller but never reaching zero (so that 1+ is never equal to one). This is also true for symbolic packages, as they are able to perform exact arithmetic. However, computer languages and packages are usually designed to store numbers as a series of decimal digits (the mantissa) combined with a factor of 10n (n is the exponent). For example, π might be stored as: 3.14159265358979 ∗ 100 . This is merely an approximation of π, as π is an irrational number and can not be written exactly using a finite number of digits. If we restrict ourselves to a fixed number of mantissa digits then we are effectively approximating the real number line with a series of discrete points. The computer is unable to distinguish between two real numbers very close together, and they are stored as the same value. The code listed above illustrates this by finding a value of for which MATLAB is unable to distinguish between 1 and 1 + . With the default MATLAB settings this value turns out to be 1.1102 ∗ 10−16, which may seem small enough to neglect but in some circumstances can be significant! The machine precision is the largest number m such that 1.0 + δ = 1.0, whenever |δ| < m Thus we know that the default machine precision in MATLAB is larger1 than 1.1102 ∗ 10−16 . Mathematically, the equation 1.0 + δ = 1.0 has only the solution δ = 0, so that m is identically equal to zero. But, because computers use floating-point arithmetic and not exact arithmetic, m > 0 for numbers stored on a computer. 7.4.1 Consequences The real problems arise when performing a large number of operations, or operations that rely on the least significant digits of numbers. For example, consider the following: 1 Q: Why larger? Why is it not equal to 1.1102 ∗ 10−16 ? COMO 102: Lecture 7, 2003 50 >> 10000000000000009999 - 10000000000000000000 ans = 10240 >> 10000000000000000000 + 10240 ans = 1.0000e+019 Clearly, when implementing numerical algorithms, it will always pay to consider whether there will be any serious subtractive cancellation or particular sensitivity to round-off error. These problems can often be avoided by algebraic rearrangement of the expressions being evaluated. Other problems can arise when we reach the limits of the exponent. Because the exponent must be stored using a fixed number of digits, we have a corresponding maximum and minimum number that can be entered. In MATLAB numbers as big as 10309 are indistinguishable from ∞: >> 10^308 ans = 1.0000e+308 >> 10^309 ans = Inf This behaviour is called “overflow”. Similarly we have “underflow”, where the exponent is negative and so large in magnitude that it can not be stored: >> 10^(-324) ans = 0 Avoiding over- and underflow is also important when considering how to enter an expression. Often some simple rearrangement (producing mathematically identical expressions) can have drastic effects on accuracy when entered in a computer! 51 COMO 102: Lecture 7, 2003 7.4.2 Example: Modulus of a complex number Mathematically we write the modulus of a complex number x + i y as: |x + i y| = q x2 + y 2 . Entering it in this form on computer is generally a bad idea, because for large x the x2 term can easily ‘overflow’. A simple rearrangement can pull out a factor x: q x2 + y2 = = v u u tx2 √ x2 y2 1+ 2 x s s 1+ = |x| 1 + ! 2 y x 2 y x and similarly we can pull out a factor of y: q x2 + y 2 = |y| v u u t x 1+ y !2 . These forms can fail if the denominator is zero, and cause overflow if the denominator is extremely small, so if |x| > |y| we use the first expression, and if |y| > |x| we use the second. Question: why are we less concerned about underflow? Lecture 8 : Programming in C Chapter 8 A list of some of the C resources on the internet can be found at: http://www.lysator.liu.se/c/c-www.html Programming in C 8.1 Basic Concepts A computer is told what to do by programs written by humans. This means a programming language must both be understandable for people and easily translatable to the basic computer instructions (“machine code”). A programming language, like any other language, has rules about structure, use of words and spelling. The big difference between human and computer language is that a computer cannot guess what is meant if an error is made. Therefore, rules in a computer language are very strict. 8.1.1 Statements A program consists of simple, basic instructions called statements. These statements perform the basic tasks such as performing calculations and outputting results to the screen. 8.1.2 Data and variables Of course, statements alone do not make a program; data must be imported, checked, manipulated and stored. This is done by the use of variables. A variable is a certain part of memory that has been given a name and in which data can be saved, by assigning a value through the variable-name. For example, a = 5 + 3; would result in the memory address with the label a storing the value 8. Just like in MATLAB, variables can be used to assign values to other variables, e.g. a = 3; c = 5 * a; would result in a storing the value 3, and c storing 15. 52 COMO 102: Lecture 8, 2003 8.1.3 53 Declarations Like in many languages, a C programmer must inform the computer of the names of the variables and their types. There are several distinct types for variables, including int (integers), double (double precision floating point numbers), and char (characters). Additionally, the procedures and functions to be used must be declared - the programmer states the names of the procedures and functions, the input parameters that they require and the type of the value each returns. The variables and procedures are declared before their use. 8.1.4 Pointers Depending on the type of the data, a variable can be a number, a string of characters, or something more complicated. Pointers are a special type of variable because they do not store a directly useful value but instead point to the memory address in which such a value is stored. At first this seems unnecessary - why do we care where the data is stored? However we shall see that there are some cases where pointers are very useful. Pointers are one of the strengths of C. They also are the cause of many catastrophic bugs in C programs, largely because they allow direct modification of memory contents with only minimal error checking. Pointers allow very flexible data structures to be created, from simple binary trees and linked lists to large customised structures tailored to a specific application. Without pointers C is very limited. For example, a maximum size for every matrix that is used must be specified ahead of time, and functions can only modify a single variable. While C++ has a few nice ways to get around these problems, we’ll be sticking to straight C in this course and so we need to know a little about pointers. Their introduction will be gentle, but you’ll need to keep your wits about you whenever you use them. 8.1.5 Arrays If you want to use many variables of the same type for the same purpose, you could, of course, declare them all separately: a1, a2, a3, a4... However there is a better way for dealing with this; by using arrays. These are sequences of a number of variables of the same type, addressed by one single name. The selection of the specific variable from the sequence is done using an integer index. For example, if d is an array, d[0] would address the first element, d[1] the second, etc. There is another great advantage in arrays - you can use calculations to determine which element is to be used. COMO 102: Lecture 8, 2003 54 For example, consider the following code, which stores the value 501 in all of the even elements of myarray from 0 to 20. What value do you think will be stored in the odd elements? int myarray[21]; for (index = 0; index <= 10; index++) myarray[index * 2] = 501; It is not hard to imagine what this code would look like if you had to change all those entries one by one! An important thing to note here is that arrays are indexed starting at zero not one (i.e. the first entry in myarray is myarray[0] not myarray[1]). Thus the declaration int myarray[21]; creates an array called myarray that holds 21 elements, indexed from myarray[0] through to myarray[20]. Higher dimensional arrays are allowed; if d is declared as a two-dimensional array one could access d[0][0] and d[0][1] (first two elements from the first row of the corresponding ‘matrix’), but also d[2][0] and d[2][1] for example (from the third row). Arrays of higher dimensions are also allowed. It should be clear that these one dimensional arrays are closely related to vectors, and two dimensional arrays are closely related to matrices. Unlike MATLAB, C has no inbuilt matrix or vectorised operations, and manipulation of arrays is often performed using loops (for loops, while loops etc.). While not so obvious, arrays in C are closely related to pointers. We’ll discuss this more later. 8.1.6 Functions Like in MATLAB, the C language allows the creation of functions. • Functions have their own local variables that can’t be accessed outside of the function! • Like in MATLAB, C functions are called using their name. • C has a few inbuilt functions and you can load ‘libraries’ that contain further commonly used functions (such as sin and fprintf). • A function can call another function. In most languages, including C, it can even call itself (this is called recursion)! • Every C program has a ‘main’ function. This function is automatically called when the program is run. 8.1.7 Local and global variables Just like with function m-files in MATLAB, local variables can only be used inside the function in which they are declared. Outside of that function they do not exist, and attempts to use them from another function will generate errors. Global variables, which are defined at the start of the code before the first function, can be used throughout the whole program. A global variable is available COMO 102: Lecture 8, 2003 55 to all functions. This sounds convenient, however their use is generally considered to be a poor programming practice. We’ll avoid global variables in this paper. 8.1.8 Parameters Functions and procedures often require some input to work, usually a small collection of variables whose values are set in the calling routine. These are called ‘parameters’, and are placed after the name of the function/procedure. The code a = minimum(3, -1, 4); would call the function minimum, pass the values 3, -1 and 4 to it and put the result of the function (-1 in this case) into the variable a. Minimum might be written like this: int minimum(int n1, int n2, int n3) { if (n1<n2 && n1<n3) return n1; if (n2<n3) return n2; return n3; } Here minimum is declared to take three parameters, all integers (the type is int), and return one integer. Note that every parameter is declared to be a particular type. The curly braces represent the beginning and end of the function. 8.2 A Short History of the Language C Contrary to most other programming languages the name of the language C is not the name of a long gone mathematician or the first letters of the words which describe the language. The reason C is called C is very simple: it’s the successor of the language ‘B’. B got its name from the place of its origin: Bell Laboratories. The creator of C worked at the Bell Laboratories as well. As a matter of fact, C is loosely based on B. The reason they gave a new name to this version rather than make it a new version of B is that the improvements made were very substantial and arguably, at the time they were made, revolutionary. These changes were important enough to keep C alive for over 17 years. This powerful language has survived several revisions, including the object-orientated C++, and is so valuable that its demise is not in sight (while B is never heard of anymore). The first version of C was written and implemented by D.M. Ritchie. It was first published in The C Programming Language by B.W. Kernighan & D.M. Ritchie in 1978. Many features of C were very vaguely, or not at all, described at that time. This caused many incompatible versions of C to be created. Many of these versions were linked to one certain type of computer, making programs written for one computer unusable for another. In 1983, however, the American National Standards Institute (ANSI) founded a commission (X3J11) to define an unambiguous and computer-independent version of C. Not so surprisingly, this version was called ANSI C. This version of C has COMO 102: Lecture 8, 2003 56 some important improvements to the one defined by Ritchie. There are ANSI C compilers available for virtually any type of computer. Since the ’release’ of ANSI C, C++ has been developed and has become very popular. C++ is an object orientated language, with classes, inheritance, and so on. Most of the structure and style of ANSI C is retained in C++, and a C++ compiler can be used to compile C programs. You may wonder why we are not learning C++ in this course, given that it is better structured and produces code that is far easier to reuse. The answer is that C is considerably faster to learn, as there are far fewer ideas to cover, and without the overhead of an object-orientated language we are able to quickly write small programs which actually achieve something rather than spending a great deal of time just setting things up in a nice way. 8.3 8.3.1 A Few Characteristics of the Language C Compiler-based C is a compiler-based language. This means that all ’grammatical’ errors must be out of the program before it will do anything. It also means that it behaves exactly the same outside the development environment as it does inside. 8.3.2 Extensive Use of Libraries Standard C on its own only has operators and a very small set of statements such as if, while and for. Statements to write to the screen and get input from the keyboard, to retrieve the time and date, work with files, and so forth are not implemented in standard C. They can be imported with the use of “header files” and “libraries” of functions. Advantages: You can choose your own sets of statements to be ‘embedded’ in the ‘running versions’ of your programs, keeping those ‘running’ files nice and small. Disadvantages: To get the ‘running versions’ running, the library files must either be included in the executable (“static linking”), or the exact version of the library that was used when compiling the program must also be available on the computer running the program (“dynamic linking”). 57 COMO 102: Lecture 8, 2003 8.3.3 Shorthand for symbols that are used often In many languages every statement, operator and symbol is addressed by a word. C often uses symbols of only one or two characters for statements that are used often. MATLAB also has compact notation. For example: or and not begin end 8.4 MATLAB | & ~ (not used) end C || && ! { } Libraries In C it is possible to cut the source code of a program in pieces. For example, you can have one file with the main program, one with routines for the screen, one with routines for files and one for the other routines. These files can be compiled separately. When a file containing nothing but supporting routines is compiled in a certain way, this compiled file is called a library. In order for the compiler to be able to check that you are using the library routines in the correct manner they need to be “declared”. Clearly only the information from the first line of the routine is needed for this checking, for example, consider the routine: int minimum(int a, int b, int c) { if (a<b && a<c) return a; if (b<c) return b; return c; } The first line conveys the important usage information: a routine called ‘minimum’ is declared, which accepts as input three integers, and returns an integer. This routine can be declared with the line: int minimum(int a, int b, int c); Given this information the compiler knows the input and output of the routine, and can perform error checking in a useful way. A library consists of many routines, and the list of the declarations for those routines are contained in a header file. Thus if we are using a library we need to include the header file in our compilation process so that the compiler knows how the routines should be used! We will see these ideas in action soon. COMO 102: Lecture 8, 2003 8.5 8.5.1 58 A Sample C Program Source Code /∗.This.is.the.program.wuffwuff. ...The.slash.star.at.the.start.of.the.line.indicates.that.this.is.a.comment.∗/ /∗.First.we.include.the.standard.input/output.library.header,.stdio.h. ...This.library.header.is.needed.to.declare.the.printf.function.............∗/ #include.<stdio.h> int.plusone(int.a) {................................/∗.plus.one.adds.one.to.an.integer.........∗/ ....int.b;......................../∗.declare.a.local.variable.b,.of.type.int.∗/ ....b.=.a.+.1;......................../∗.set.b.to.be.a.plus.1............∗/ ....return.b;......................../∗.return.the.value.stored.in.b....∗/ } int.main(void) {................................/∗.the.main.routine..∗/ ....int.a;......................../∗.declare.a.local.variable.a,.of.type.int..∗/ ....printf("Welcome.to.Wuffwuff!\n");......../∗.print.a.message.to.the.screen..∗/ ....a.=.7; ....printf("7.+.1.=.%d\n",.plusone(a));......../∗.print.the.output.of.plusone(a).∗/ ....return.0;......................../∗.main.always.returns.0..∗/ } 8.5.2 Compilation and Output [j@bluebox lectures]$ gcc -Wall -o wuffwuff wuffwuff.c [j@bluebox lectures]$ ./wuffwuff Welcome to Wuffwuff! 7 + 1 = 8 Lecture 9 : Programming in C Part II Chapter 9 9.1 Variables in C Programming in C Part II C provides the programmer with FOUR basic data types. int float double char - integer numbers. floating point numbers (single precision) floating point numbers (double precision) a character User defined variables must be declared before they can be used in a program. Get into the habit of declaring variables using lowercase characters. Remember that C is case sensitive, so even though the two variables sum and Sum have the same name, they are considered to be different variables in C. 9.1.1 Variable Declaration The declaration of variables is done after the opening brace of main(), #include <stdio.h> main() { int sum; sum = 500 + 15; printf("The sum of 500 and 15 is %d\n", sum); } Sample Program Output The sum of 500 and 15 is 515 It is possible to declare variables elsewhere in a program, but lets start simply and then get into variations later on. The basic format for declaring variables is data_type var, var, ... ; where data type is one of the four basic types, an integer, character, float, or double type. The program declares the variable sum to be of type INTEGER (int). The variable sum is then assigned the value of 500 + 15 by using the assignment operator, the = sign. sum = 500 + 15; 59 COMO 102: Lecture 9, 2003 9.1.2 60 Printf Now lets look more closely at the printf() statement. It has two arguments, separated by a comma. Lets look at the first argument, "The sum of 500 and 15 is %d\n" The % sign is a special character in printf statements in C. It is used to display the value of variables. When the statement is executed, C starts printing the text until it finds a % character. If it finds one, it looks up the next argument (in this case sum), displays its value, then continues on. The d character that follows the % indicates that an integer is expected. So, when the %d sign is reached, the next argument to the printf() routine is looked up and displayed (in this case the variable sum, which is 515). The \n is then executed which prints the newline character. . 9.1.3 Formatting Arguments of Printf Some of the formatters for printf are, Cursor Control Formatters \n newline \t tab \r carriage return \f form feed Variable Formatters %d integer %c character %s string or character array %f float %e double The following program prints out two integer values separated by a TAB. It does this by using the \t cursor control formatter. #include.<stdio.h> int.main() { ....int.sum,.value; ....sum.=.10; ....value.=.15; ....printf("%d\t%d\n",.sum,.value); ....return.0;.................../∗.normal.termination.∗/ } COMO 102: Lecture 9, 2003 [j@aythya lectures]$ gcc sumvalue.c -o sumvalue [j@aythya lectures]$ ./sumvalue 10 15 9.2 Parameters in Subroutines Consider the following code, which contains the sub-function ‘frazzle’. #include.<stdio.h> /∗.Frazzle.uses.a.LOCAL.COPY.of.a,.doubles.it,.then ........prints.out.the.value..∗/ void.frazzle(int.a) { ....a.=.a.∗.2; ....printf("In.frazzle:..a.=.%d\r\n",.a); } int.main(void) { ....int.a=1;.................../∗.initialise.a.to.be.1.∗/ ....printf("In.main:.....a.=.%d\r\n",.a); ....frazzle(a); ....a.=.a.+.2;................../∗.add.2.to.the.current.value.of.a.∗/ ....printf("In.main:.....a.=.%d\r\n",.a); ....return.0; } What values will this code output to the screen? Either: In main: In frazzle: In main: a = 1 a = 2 a = 3 Or: In main: In frazzle: In main: a = 1 a = 2 a = 4 61 COMO 102: Lecture 9, 2003 9.3 9.3.1 62 Pointers Allowing Permanent Changes in Parameters We can make permanent changes to parameters, rather than modifying a local copy, by passing pointers to variables. Then the a local copy of the pointer is made, but we can still access (and permanently change) the data being pointed to! This is especially useful when we want a function to generate several values, as C only allows us to return a single variable from a function. For example: #include.<stdio.h> /∗.swaps.two.variables.passed.as.POINTERS.to.integers.∗/ void.swap(int.∗aptr,.int.∗bptr) { ....int.c; ....c.=.(∗aptr);................/∗.(∗aptr).‘dereferences’.the.pointer,.∗/ ....(∗aptr).=.(∗bptr);........../∗.giving.access.to.the.memory.........∗/ ....(∗bptr).=.c;................/∗.contents.(the.integer)!.............∗/ } int.main() { ....int.n1,.n2; ....n1.=.1; ....n2.=.2; ....printf("n1.=.%d,.n2.=.%d\n",.n1,.n2); ....swap(&n1,.&n2);............./∗.the.ampersand.creates.a.pointer.from ...................................a.variable..........................∗/ ....printf("n1.=.%d,.n2.=.%d\n",.n1,.n2); ....return.0;.................../∗.normal.termination.∗/ } [j@aythya lectures]$ gcc -o swap -Wall swap.c [j@aythya lectures]$ ./swap n1 = 1, n2 = 2 n1 = 2, n2 = 1 Why can’t we just go aptr=bptr in the swap routine instead of (*aptr)=(*bptr)? COMO 102: Lecture 9, 2003 9.3.2 63 Pointer Values #include.<stdio.h> int.main() { ....int.a;....................../∗.an.integer.∗/ ....int.∗aptr;................../∗.a.pointer.to.an.integer.∗/ ....a.=.27; ....aptr.=.&a;................../∗.aptr.now.points.to.a.∗/ ....printf("a.=.%d,.(∗aptr)=%d,.aptr.=.%p.\n",.a,.(∗aptr),.aptr); ....return.0; } [j@aythya lectures]$ gcc -o pointy -Wall pointy.c [j@aythya lectures]$ ./pointy a = 27, (*aptr)=27, aptr = 0xbffffaa4. Here we see that a has the value 27, the pointer aptr points to an integer with value 27, and that the memory address of this value being pointed to is 0xbffffaa4. 9.3.3 Pointers are Useful but *DANGEROUS*! #include.<stdio.h> int.main() { ....int.a.=.27; ....int.∗aptr; ....aptr.=.&a; ....aptr.=.aptr.+.37;.........../∗.we.should.have.used..∗aptr.=.∗aptr.+.37.∗/ ....printf("a=%d,.∗aptr=%d.\n",.a,.∗aptr); ....return.0; } [j@aythya lectures]$ gcc -o badbadbad -Wall badbadbad.c [j@aythya lectures]$ ./badbadbad a=27, *aptr=-1073742635. COMO 102: Lecture 9, 2003 9.3.4 64 Making Pointers Safer Pointers can be defined as constants, so that the memory address of the pointer can not be changed. The data that the pointer points to is still able to be modified. This reduces the likelihood of obscure programming errors considerably! Rewriting our swap routine using this we get: #include.<stdio.h> /∗.Swaps.two.variables.passed.as.CONSTANT.pointers.to.integers..∗/ /∗.The.‘const’.keyword.means.the.pointers.can.not.be.changed,...∗/ /∗.however.the.contents.of.the.pointers.can.still.be.modified!..∗/ void.swap(int.∗const.aptr,.int.∗const.bptr) { ....int.c; ....c.=.(∗aptr);................/∗.(∗aptr).‘dereferences’.the.pointer.∗/ ....(∗aptr).=.(∗bptr); ....(∗bptr).=.c; } int.main() {.............................../∗.main.routine.same.as.before..∗/ ....int.n1,.n2; ....n1.=.1; ....n2.=.2; ....printf("n1.=.%d,.n2.=.%d\n",.n1,.n2); ....swap(&n1,.&n2); ....printf("n1.=.%d,.n2.=.%d\n",.n1,.n2); ....return.0;.................../∗.normal.termination.∗/ } [j@aythya lectures]$ gcc -Wall -o goodswap goodswap.c [j@aythya lectures]$ ./goodswap n1 = 1, n2 = 2 n1 = 2, n2 = 1 Now if we accidentally use something like aptr=bptr instead of (*aptr)=(*bptr) in the swap routine we get the following compilation error: goodswap.c: In function ‘swap’: goodswap.c:10: warning: assignment of read-only location COMO 102: Lecture 9, 2003 9.3.5 65 Some Friendly Advice In summary, when using pointers to change variables passed to functions: • Always use -Wall when compiling your code, and always fix any sources of warning messages! • Always use const to prevent accidental and difficult to debug pointer problems. 9.4 Bug Finding and Indenting The indent linux program makes your source code easier to read. The lclint program helps find bugs in your code that the compiler may miss and also identifies a range of poor programming practices. Consider, for example, the following ugly program: #include.<stdio.h> /∗.An.ugly.piece.of.code.that.we’ll.tidy.with.indent.∗/. int.main().{.int.a; a.=.7;.a=a∗a;.a=a∗a; printf("a=%d\n",a);.} 9.4.1 lclint [j@crusher lectures]$ lclint ugly.c ugly.c: (in function main) ugly.c:6:22: Path with no return in function declared to return int There is a path through a function declared to return a value on which there is no return statement. This means the execution may fall through without returning a meaningful result to the caller. (Use -noret to inhibit warning) Finished checking --- 1 code warning 9.4.2 indent We use the “-kr” flag to turn on Kernighan and Ritchie coding style, which is a little nicer than the default indenting, and “-o” to specify an output file. [j@crusher lectures]$ indent -kr -o notugly.c ugly.c COMO 102: Lecture 9, 2003 66 #include.<stdio.h> /∗.An.ugly.piece.of.code.that.we’ll.tidy.with.indent.∗/ int.main() { ....int.a; ....a.=.7; ....a.=.a.∗.a; ....a.=.a.∗.a; ....printf("a=%d\n",.a); } Note that indent is fairly safe and you generally don’t need to use the “-o” flag to specify a different output file. However, the use of “-kr” is highly recommended. Lecture 10 : Programming in C Part III Chapter 10 10.1 Arrays Programming in C Part III As we have seen in Exercise 8, arrays are indexed starting from zero and elements are accessed with square bracket notation. For example: #include.<stdio.h> void.arraydemo(double.anumber) { ....double.myarray[10];........./∗.indexed.from.0.through.to.9.∗/ ....int.i; ....for.(i.=.0;.i.<=.9;.i++) ........myarray[i].=.anumber.∗.(double).i;....../∗.convert.i.to.‘double’.∗/ ....for.(i.=.0;.i.<=.9;.i++).{ ........if.(i.>.0.&&.i.%.3.==.0)......../∗.If.i.is.3.or.6.or.9.or.....∗/ ............printf("\n"); ........printf("myarray[.%d.].=.%5.1f\t",.i,.myarray[i]); ....} ....printf("\n"); } int.main(void) { ....arraydemo(7.5); ....return.0;.................../∗.0.return.value.=.normal.termination..∗/ } produces the output: [j@aythya lectures]$ gcc -Wall ademo.c -o ademo [j@aythya lectures]$ ./ademo myarray[ 0 ] = 0.0 myarray[ 1 ] = 7.5 myarray[ 2 ] = myarray[ 3 ] = 22.5 myarray[ 4 ] = 30.0 myarray[ 5 ] = myarray[ 6 ] = 45.0 myarray[ 7 ] = 52.5 myarray[ 8 ] = myarray[ 9 ] = 67.5 67 15.0 37.5 60.0 COMO 102: Lecture 10, 2003 68 The expression (double)i converts (‘casts’) the integer value of i to a double, so that the multiplication is performed arithmetic. (Integer arithmetic discards all fractional portions of the numbers.) To be safe, if you want a floating point answer then you should convert all integers in a mixed expression to doubles using (double) before the variable. 10.1.1 Passing Arrays to Subroutines Consider the following main program: #include.<stdio.h>............../∗.for.printf().∗/ #include.<stdlib.h>............./∗.for.rand()...∗/ #include."sorter.h"............./∗.our.sorting.header.file!.∗/ int.myrandom(int.a,.int.b) {.............................../∗.find.a.random.number.between.a.and.b.∗/ ....double.d.=.b.−.a.+.1.0,.t.=.RAND MAX.+.1.0; ....return.(a.+.(int).(d.∗.rand()./.t)); } int.main(void) { ....const.int.Max.=.6; ....int.i,.a[Max]; ....printf("This.program.will.sort.%d.values:\n",.Max); ....for.(i.=.0;.i.<.Max;.i++) ........a[i].=.myrandom(1,.100); ....print array(a,.Max); ....bubble sort(a,.Max); ....printf("Sorted:\n"); ....print array(a,.Max); ....return.0; } Rather than include all of the routines in one file, this program is broken into two parts. The main code is in bubbles.c, and auxiliary sorting routines are declared in sorter.h and defined in sorter.c. The main routine shown above uses a routine called ‘bubble sort’ which is in the sorter files. Since we include sorter.h at the start of the above code, the compiler knows about bubble sort. COMO 102: Lecture 10, 2003 69 /∗.This.is.the.header.file.for.the.sorter.routines, ...which.are.defined.in.the.sorter.c.file...............∗/ void.swap(int.∗a,.int.∗b); void.bubble sort(int.a[],.int.num); void.print array(int.a[],.int.num); In the header file above three functions are declared - swap, bubble sort and print array. Note that the int a[] arguments of bubble sort and print array allow passing of an array to the functions without specifying a constant size for the array. We want the code to sort arrays of various sizes, so we pass a parameter num, which gives the number of elements in the array a (a[0] through to a[num-1]). #include.<stdio.h>............../∗.for.printf.(in.print array).∗/ #include."sorter.h"............./∗.include.our.previous.declarations,. ...................................from.the.sorter.h.header.file.−.this ...................................is.important.for.consistency!.∗/ void.swap(int.∗const.a,.int.∗const.b) {.............................../∗.swap.two.numbers.(using.pointers).∗/ ....int.t; ....t.=.∗a; ....∗a.=.∗b; ....∗b.=.t; } void.bubble sort(int.a[],.int.num) {.............................../∗.A.bad.algorithm.for.sorting.an.array.∗/ ....int.x,.y; ....for.(x.=.0;.x.<.num.−.1;.x++) ........for.(y.=.0;.y.<.num.−.1.−.x;.y++) ............if.(a[y].>.a[y.+.1]) ................swap(&a[y],.&a[y.+.1]); } void.print array(int.a[],.int.num) {.............................../∗.print.out.an.array.of.numbers.∗/ ....int.i; ....printf("Array.=.{.%d",.a[0]); ....for.(i.=.1;.i.<.num;.i++) ........printf(",.%d",.a[i]); ....printf(".}\n"); } COMO 102: Lecture 10, 2003 70 Compiling and running the code produces the following. Note that we need to specify both bubble.c and sorter.c on the compilation line. [j@crusher lectures]$ gcc -o bubble -Wall bubble.c sorter.c [j@crusher lectures]$ bubble This program will sort 6 values: Array = { 85, 40, 79, 80, 92, 20 } Sorted: Array = { 20, 40, 79, 80, 85, 92 } 10.2 Writing output to a file: fprintf Writing output to a file consists of three steps: 1. Opening the file (fopen). 2. Writing the data to the file (fprintf). 3. Closing the file (fclose). For example, consider the following code: #include.<stdio.h> int.main(void) { ....FILE.∗myfile;.............../∗.A.pointer.to.a.FILE.for.fopen.etc..∗/ ....myfile.=.fopen("output.txt",."w");../∗.try.to.open.the.file.∗/ ....fprintf(myfile,."Hello!.This.is.my.output.file!\n"); ....fprintf(myfile,."3.+.4.=.%d\n",.3.+.4); ....fclose(myfile); ....return.0; } After running it we can view the output file either with nedit, or with the command ‘more’: [j@bluebox lecture9]$ more output.txt Hello! This is my output file! 3 + 4 = 7 The line ‘FILE * myfile’ declares the variable ‘myfile’ to be a pointer to a FILE. We then assign myfile to be a newly opened file with filename ‘output.txt’ (the “w” indicates it be opened for writing, an “r” would open it for reading ). If COMO 102: Lecture 10, 2003 71 for any reason the file was not able to be opened for writing myfile will be set to ‘NULL’, and we could test for this if we wished. fprintf works identically to printf except that we specify the file to write to as a new first argument. When we are finished printing to the file we close it with the ‘fclose’ command. 10.2.1 Exporting Data from C to MATLAB #include.<stdio.h> #include.<math.h> /∗.This.must.be.compiled.with: ........gcc.−lm.−Wall.−o.tomatlab.tomatlab.cc. ...−lm.forces.the.inclusion.of.the.math.library. ........which.is.needed.for.the.sin(x)..................∗/ int.main(void) { ....FILE.∗f; ....double.x; ....const.double.pi.=.3.141592654; ....f.=.fopen("xyvals.txt",."w"); ....for.(x.=.0.0;.x.<=.2.∗.pi;.x.=.x.+.pi./.10.0) ........fprintf(f,."%e.%e\n",.x,.sin(x)); ....fclose(f); ....return.0; } When compiled and run, the tomatlab code produces the following data: [j@crusher lectures]$ more xyvals.txt 0.000000e+00 0.000000e+00 3.141593e-01 3.090170e-01 6.283185e-01 5.877853e-01 9.424778e-01 8.090170e-01 1.256637e+00 9.510565e-01 1.570796e+00 1.000000e+00 1.884956e+00 9.510565e-01 2.199115e+00 8.090170e-01 2.513274e+00 5.877853e-01 2.827433e+00 3.090170e-01 3.141593e+00 -4.102064e-10 (etc) 72 COMO 102: Lecture 10, 2003 And we can import and graph it in MATLAB with the following script m-file: %JPLOT.Load.xyvals.txt.generated.by.the.C.program.and.graph.them! load.-ascii.xyvals.txt x=xyvals(:,1);.....%.the.first.column.is.the.x.values. y=xyvals(:,2);.....%.the.second.column.is.the.y.values. plot(x,y); axis.tight; 1 0.8 0.6 0.4 0.2 0 −0.2 −0.4 −0.6 −0.8 −1 0 1 2 3 4 5 6 COMO 102: Lecture 10, 2003 10.3 73 Recursion A function may call itself, and if it does it is said to be recursive. Consider the following code, which shows two methods of calculating factorials. #include.<stdio.h> /∗.Calculates.n!.=.n∗(n−1)∗(n−2)∗.....∗3∗2∗1.∗/ int.factorial recursive(int.n) { ....if.(n.==.0) ........return.1; ....else ........return.n.∗.factorial recursive(n.−.1); } /∗.Also.calculates.n!.=.n∗(n−1)∗(n−2)∗.....∗3∗2∗1.∗/ int.factorial loop(int.n) { ....int.i; ....int.r; ....r.=.1; ....for.(i.=.2;.i.<=.n;.i++) ........r.=.r.∗.i; ....return.r; } int.main() { ....int.n.=.12; ....printf("Recursive:.%d!.=.%d.\n",.n,.factorial recursive(n)); ....printf("Loop:......%d!.=.%d.\n",.n,.factorial loop(n)); ....return.0; } [j@crusher [j@crusher Recursive: Loop: lectures]$ gcc -o factorial -Wall factorial.c lectures]$ factorial 12! = 479001600. 12! = 479001600. Lecture 11 : Programming in C Part IV Chapter 11 the fundamentals of C programming we will look at Now that we have explored writing code that is integrated into a larger system. This weeks work will focus on the client-server “comotank” software, written specifically for this course. We will write functions that control computer simulated hovertanks which obey a limited set of physical laws. While the topic may seem a little frivolous, rest assured that the programming skills and mathematics that you will learn this week are widely applicable. Programming in C Part IV 11.1 The Comotank Client-Server Framework The comotank software runs as a ‘server’ program. The server launches multiple ‘client’ programs, where each client controls a simulated hovertank. The clients are stand-alone C programs, which make use of a few simple functions (provided for you) that communicate with the server. 11.1.1 The Rules 1. The hovertanks reside in a square arena, with coordinates in the range (−10, 000, −10, 000) through to (10, 000, 10, 000). 2. The tanks are circular (as seen from above) and have a radius of 300 units. 3. They can be accelerated up to a maximum velocity of 120 units per second. 4. They can fire from a centrally mounted turret, which can rotate independently of the tanks direction. 5. The shots travel at 175 units per second and are unaffected by the tanks velocity when the shot is fired. 6. A shot directly hitting a tank will completely disable (kill) it. 7. The disabled tank acts as an obstacle for other tanks and shots. It can be completely destroyed with a further three shots, removing it from the arena. 8. Tanks colliding with other tanks or walls are not damaged, but come to a complete stop (instantly). 9. In a match points are awarded for disabling tanks (one per kill). A bonus of n/3 points is awarded to the last surviving tank, where n is the initial number of tanks in the arena. 10. Tanks can see down a narrow field of vision centred on their turret (within radians either side of the turret). 74 π 20 COMO 102: Lecture 11, 2003 11.1.2 75 An Empty Client When you first write your own hovertank controlling program you will start with a copy of empty.c, which is shown below. #include.<math.h> #include.<stdlib.h> const.double.Pi.=.3.1415926535897932385; /∗.Use.these.functions.to.control.your.tank..∗/ void.accelerate(double.angle);../∗.in.radians......∗/ void.turn turret(double.delta angle);.../∗.in.radians......∗/ void.fire(void);................/∗.at.turret angle.∗/ /∗.These.variables.provide.information.about.your.tank...∗/ /∗.They.are.automatically.updated.every.time.you.use.one.∗/ /∗.of.the.above.three.functions..........................∗/ int.x,.y,.vx,.vy;.............../∗.position.and.velocity.∗/ double.turret angle;............/∗.in.radians.∗/ int.dist to closest enemy;....../∗.0.=.none,.>0.alive,.<0.dead.∗/ double.angle of closest enemy;../∗.in.radians.∗/ int.shots ready; /∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/ /∗.The.main.routine.for.your.robot.hover.tank!.∗/ /∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/ void.robot(void) { } During each game second the server accepts one accelerate, turn turret or fire command from each of the clients. The clients (tank controlling programs) are paused while the commands are executed. The server then updates the client variables (x,y,. . .,shots ready) to reflect the new state of each tank, and allows all of the clients to continue execution. The clients can execute arbitrarily complex code between the calls to accelerate, turn turret and fire, allowing for sophisticated targeting and movement routines. COMO 102: Lecture 11, 2003 11.1.3 76 The Controlling Functions in More Detail Accelerate This accelerates your tank at 60 units/s2 for one quarter of a second in the direction angle, which is an absolute angle in radians (measured anti-clockwise from the xaxis). Turn Turret This turns the gun turret by a specified CHANGE in angle, which must lie between π π and 20 . Angles outside this range will be ignored. To skip a turn, letting the − 20 tank glide at its current velocity, you can use turn turret(0.0). Fire If you have any shells ready to fire (check with the shots ready variable) then this fires one shell in the current turret direction. Otherwise the command is ignored, wasting a second of game time for your tank. COMO 102: Lecture 11, 2003 11.1.4 77 A Simple Tank #include.<math.h> #include.<stdlib.h> const.double.Pi.=.3.1415926535897932385; void.accelerate(double.angle);../∗.in.radians......∗/ void.turn turret(double.delta angle);.../∗.in.radians......∗/ void.fire(void);................/∗.at.turret angle.∗/ /∗.These.variables.provide.information.about.your.tank...∗/ int.x,.y,.vx,.vy;.............../∗.position.and.velocity.∗/ double.turret angle;............/∗.in.radians.∗/ int.dist to closest enemy;....../∗.0.=.none,.>0.alive,.<0.dead.∗/ double.angle of closest enemy;../∗.in.radians.∗/ int.shots ready; /∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/ /∗.sucky.c.−.the.controlling.program.for.the.sucky.tank..∗/ /∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/ void.robot(void) { ....if.(dist to closest enemy.>.0).{..../∗.Can.we.see.something?.∗/ ........if.(shots ready.>.0) ............fire();............./∗.shoot.∗/ ......../∗.Aim.−.this.wont.always.work.for.two.reasons..∗/ ........turn turret(angle of closest enemy.−.turret angle); ....}.else.{ ......../∗.look.around!.∗/ ........turn turret(Pi./.20.0); ....} } 11.1.5 Compiling and Testing This code is compiled with the following command. The generic.c file contains the definitions for accelerate, turn turret, and fire. [j@crusher run]$ gcc -Wall -o sucky sucky.c generic.c COMO 102: Lecture 11, 2003 78 The comotank program can then be run, for example with three of the ‘sucky’ tanks: [j@crusher run]$ comotank sucky sucky sucky Gamestate initialised: 3 tanks (50 max). Tank sucky (#01) KILLED by sucky (#02)! Tank sucky (#03) KILLED by sucky (dead)! Game time: 59 secs (236 cycles), [0 real seconds]. (Tank #2 wins) WINNER: sucky [1 kills]! 11.1.6 Utilities Available We can review the battle graphically with the tankview program, or run a set of matches with bigmatch. How does our tank (sucky) stack up against the notorious wuffles.mk4? Lets use bigmatch to check with 100 one-on-one battles: [j@crusher run]$ bigmatch 100 1 sucky wuffles.mk4 Battle of 100 matches, 1 of each tank type. +-----------------+---------+-----------+-----------+ | Tank | Wins | Kills | Points | +-----------------+---------+-----------+-----------+ | wuffles.mk4 | 91 | 91 | 151 | | sucky | 9 | 9 | 15 | +-----------------+---------+-----------+-----------+ And we see that wuffles.mk4 is far superior. You will have several designs to compare your tanks against, ranging from the poorly performing findshoot and tatie to the sophisticated wuffles models. Large numbers of matches can be run, as shown below: COMO 102: Lecture 11, 2003 11.1.7 79 Tankview Tankview reviews the most recently stored battle graphically. An example screenshot is shown below.
© Copyright 2025 Paperzz