(*^ ::[ Information = "This is a Mathematica Notebook file. It contains ASCII text, and can be transferred by email, ftp, or other text-file transfer utility. It should be read or edited using a copy of Mathematica or MathReader. If you received this as email, use your mail application or copy/paste to save everything from the line containing (*^ down to the line containing ^*) into a plain text file. On some systems you may have to give the file a name ending with ".ma" to allow Mathematica to recognize it as a Notebook. The line below identifies what version of Mathematica created this file, but it can be opened using any other version as well."; FrontEndVersion = "Macintosh Mathematica Notebook Front End Version 2.2"; MacintoshStandardFontEncoding; fontset = title, inactive, noPageBreakBelow, nohscroll, preserveAspect, groupLikeTitle, center, M7, bold, e8, 24, "Times"; fontset = subtitle, inactive, noPageBreakBelow, nohscroll, preserveAspect, groupLikeTitle, center, M7, bold, e6, 18, "Times"; fontset = subsubtitle, inactive, noPageBreakBelow, nohscroll, preserveAspect, groupLikeTitle, center, M7, italic, e6, 14, "Times"; fontset = section, inactive, noPageBreakBelow, nohscroll, preserveAspect, groupLikeSection, grayBox, M22, bold, a20, 18, "Times"; fontset = subsection, inactive, noPageBreakBelow, nohscroll, preserveAspect, groupLikeSection, blackBox, M19, bold, a15, 14, "Times"; fontset = subsubsection, inactive, noPageBreakBelow, nohscroll, preserveAspect, groupLikeSection, whiteBox, M18, bold, a12, 12, "Times"; fontset = text, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M7, 12, "Times"; fontset = smalltext, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M7, 10, "Times"; fontset = input, noPageBreakInGroup, preserveAspect, groupLikeInput, M42, N23, bold, B65535, L-5, 12, "Courier"; fontset = output, output, inactive, noPageBreakInGroup, preserveAspect, groupLikeOutput, M42, N23, L-5, 12, "Courier"; fontset = message, inactive, noPageBreakInGroup, preserveAspect, groupLikeOutput, M42, N23, R65535, L-5, 12, "Courier"; fontset = print, inactive, noPageBreakInGroup, preserveAspect, groupLikeOutput, M42, N23, L-5, 12, "Courier"; fontset = info, inactive, noPageBreakInGroup, preserveAspect, groupLikeOutput, M42, N23, B65535, L-5, 12, "Courier"; fontset = postscript, PostScript, formatAsPostScript, output, inactive, noPageBreakInGroup, preserveAspect, groupLikeGraphics, M7, l34, w282, h287, 12, "Courier"; fontset = name, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M7, italic, 10, "Geneva"; fontset = header, inactive, noKeepOnOnePage, preserveAspect, M7, 12, "Times"; fontset = leftheader, inactive, L2, 12, "Times"; fontset = footer, inactive, noKeepOnOnePage, preserveAspect, center, M7, 12, "Times"; fontset = leftfooter, inactive, L2, 12, "Times"; fontset = help, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M7, 10, "Times"; fontset = clipboard, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M7, 12, "Times"; fontset = completions, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M7, 12, "Times"; fontset = special1, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M7, 12, "Times"; fontset = special2, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M7, 12, "Times"; fontset = special3, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M7, 12, "Times"; fontset = special4, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M7, 12, "Times"; fontset = special5, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M7, 12, "Times"; currentKernel; ] :[font = smalltext; inactive; preserveAspect] Copyright (C) 1997 Rich Neidinger, John Swallow, and Todd Will. Free for distribution to college and university instructors for personal, non-commercial use. If these notebooks are used in a course, the authors request $20 per student. :[font = title; inactive; preserveAspect] 20. Arrays and Matrices :[font = smalltext; inactive; preserveAspect] Last revision: October 1 1996 :[font = text; inactive; preserveAspect] In this lab we introduce a useful new data type, called an array. We consider arrays, methods for their definition, initialization, and manipulation, and pay particular attention to using iterative structures to modify them. :[font = section; inactive; Cclosed; preserveAspect; startGroup] Arrays :[font = text; inactive; preserveAspect] An array is a general data type. A one-dimensional array is simply a list of elements, such as "{2,3,4}"; such an array is sometimes termed a vector. A two-dimensional array is a list of lists of elements, where the lists inside all have the same length, such as "{{1,2},{4,5},{7,8}}"; such an array is sometimes termed a matrix. A three-dimensional array is then a list of lists of lists of elements, where the innermost lists all have the same length and the lists containing those lists also all have the same length. For an example of a three-dimensional array, consider the expression "{{{1,2,3},{2,3,4}},{{3,4,5},{5,6,7}},{{7,8,9},{8,9,10}}}". Note that the innermost lists all have length 3, and the lists containing these innermost lists all have length 2 (containing 2 lists of length 3). Arrays can be defined for any positive integral dimension by continuing in the same format. ;[s] 13:0,0;3,1;8,0;37,1;58,0;145,1;151,0;156,1;177,0;327,1;333,0;339,1;363,0;903,-1; 2:7,13,9,Times,0,12,0,0,0;6,13,9,Times,2,12,0,0,0; :[font = subsection; inactive; Cclosed; preserveAspect; startGroup] Sizes :[font = text; inactive; preserveAspect] As you may surmise, arrays have a definite size. For one-dimensional arrays, the size is simply the number of elements. For two-dimensional arrays, however, the size must contain two numbers: the number of inner lists, followed by the number of elements in each list. For three-dimensional arrays, the size is composed of three numbers: the number of outermost lists, the number of lists inside a given outermost list, and finally the number of elements in each of the innermost lists. In the examples above, the one-dimensional array has size 3; the two-dimensional array has size "3,2" (or 3x2); and the three-dimensional array size "3,2,3" (or 3x2x3). ;[s] 3:0,0;42,1;47,0;661,-1; 2:2,13,9,Times,0,12,0,0,0;1,13,9,Times,2,12,0,0,0; :[font = text; inactive; preserveAspect] Mathematica can determine these sizes, using a command we have considered some time ago, "Dimensions[ ]". Let's try it: :[font = input; preserveAspect] oneDArray = {2,3,4}; twoDArray = {{1,2},{4,5},{7,8}}; threeDArray = {{{1,2,3},{2,3,4}},{{3,4,5},{5,6,7}}, {{7,8,9},{8,9,10}}}; Dimensions[oneDArray] Dimensions[twoDArray] Dimensions[threeDArray] :[font = text; inactive; preserveAspect] Mathematica also has some functions which display arrays in rectangular ways. "ColumnForm[ ]" displays a vector as a column instead of a row, and "MatrixForm[ ]" displays a two-dimensional array in rectangular form, where the inner lists are written out as rows. :[font = input; preserveAspect] ColumnForm[oneDArray] :[font = input; preserveAspect] MatrixForm[twoDArray] :[font = text; inactive; preserveAspect; endGroup] (For obvious reasons, there is no simple way to display an array of dimensionality greater than two.) :[font = subsection; inactive; Cclosed; preserveAspect; startGroup] Individual Elements :[font = text; inactive; preserveAspect] We can refer to individual elements of a list with array indices. These indices tell us how to find the element by indicating, working from the outside in, which particular list, which element of if that list, and so on, contains the element we are interested in. In a one-dimensional array, we need only mention one number, the number of elements from the beginning of the list one must count to find the element. In a two-dimensional array, we require two numbers: the first index will tell us which list to use (which "row"), the second which element of that list (which "column"). For instance, in the two-dimensional array "{{1,2,3},{4,5,6}}" the element with value 3 has indices "1,3". Similarly, in a three-dimensionalarray, the three indices pinpoint, respectively, which outer list, which list of that list, and which element of that inner list one must consider to reach the element in question. ;[s] 3:0,0;50,1;64,0;912,-1; 2:2,13,9,Times,0,12,0,0,0;1,13,9,Times,2,12,0,0,0; :[font = text; inactive; preserveAspect] To determine what value resides in an array at a particular set of array indices, we can use "Part[ ]" or its functional equivalent, "[[ ]]": :[font = input; preserveAspect] oneDArray[[2]] Part[oneDArray,2] :[font = input; preserveAspect] twoDArray[[3,2]] Part[twoDArray,3,2] :[font = text; inactive; preserveAspect] Note that if we specify too few indices, you will get a list or list of lists---all of the elements of the array whose first so many indices match our specified ones. :[font = input; preserveAspect; endGroup; endGroup] Part[threeDArray,2,2] :[font = section; inactive; Cclosed; preserveAspect; startGroup] Arrays: Declaration and Initialization :[font = text; inactive; preserveAspect] To declare that a name refers to an array, we must decide from the outset what the size of the array will be and what values we will initially assign to its elements. Initializing an array to be an array of zeros is simplest, and we will begin thus. To initialize a one-dimensional array of zeros, we use a "Table[ ]" command. :[font = input; preserveAspect] myArray = Table[0,{10}] :[font = text; inactive; preserveAspect] For two-dimensional and three-dimensional arrays, we use multiple iterators. :[font = input; preserveAspect] my2 = Table[0,{10},{3}] :[font = input; preserveAspect] my3 = Table[0,{3},{2},{3}] :[font = text; inactive; preserveAspect] It may happen that we know some nonzero values to which we would like the array initialized. We can use "Table[ ]" to initialize the values of the various entries to the results of some function or expression, which may depend on the indices of the entries. We might want to initialize each entry of a 3x5 array to its own row number; in that case, we would execute :[font = input; preserveAspect] my4 = Table[i,{i,3},{j,5}] :[font = text; inactive; preserveAspect] Similarly, to initialize a 4x6 array with value "i+2^j" at indices "i,j", we would execute :[font = input; preserveAspect] Clear[f] f[i_,j_]:=i+2^j; my5 = Table[f[i,j],{i,4},{j,6}] :[font = text; inactive; preserveAspect] or :[font = input; preserveAspect] my5 = Table[i+2^j,{i,4},{j,6}] :[font = text; inactive; preserveAspect] It is also possible to define special sorts of arrays in Mathematica. An identity matrix is a two-dimensional array of size "n,n" which contains all zeros, except that the elements with two identical indices have value 1. Mathematica can create such a matrix using the "IdentityMatrix[ ]" command. ;[s] 3:0,0;74,1;90,0;301,-1; 2:2,13,9,Times,0,12,0,0,0;1,13,9,Times,2,12,0,0,0; :[font = input; preserveAspect] IdentityMatrix[5] :[font = text; inactive; preserveAspect] More generally, to specify a diagonal matrix, which is a square two-dimensional array which contains all zeros, except that the elements with two identical indices may have nonzero values. Mathematica can create such a matrix using the "DiagonalMatrix[ ]" command. ;[s] 3:0,0;29,1;44,0;267,-1; 2:2,13,9,Times,0,12,0,0,0;1,13,9,Times,2,12,0,0,0; :[font = input; preserveAspect] DiagonalMatrix[{1,2,3}] :[font = text; inactive; preserveAspect; endGroup] Can you tell how Mathematica decides what size the diagonal matrix should be? :[font = section; inactive; Cclosed; preserveAspect; startGroup] Arrays: Modification :[font = text; inactive; preserveAspect] We can modify the value of any array element simply by executing an assignment statement, referring to an element via its indices. :[font = input; preserveAspect] oneDArray :[font = input; preserveAspect] oneDArray[[2]]=500 :[font = input; preserveAspect] oneDArray :[font = text; inactive; preserveAspect] In many cases, though, instead of modifying a particular array's elements, we may wish to have a copy of the old array made, and then modify the elements in the new array. (One particular case where we will need to do so occurs when an array is an actual parameter in a function call; since we cannot modify the value of a formal parameter inside a function, we must copy the formal parameter to a local variable in order to permit modifications.) We can do so by using an assignment statement to complete the first copy of matrices. :[font = input; preserveAspect] twoDArray :[font = input; preserveAspect] twoD2 = twoDArray; twoD2[[3,2]]=0; twoD2 :[font = text; inactive; preserveAspect] Arrays are frequently manipulated using iterative structures such as "For[ ]", "Do[ ]", and "Table[ ]". Here are some examples. :[font = subsection; inactive; Cclosed; preserveAspect; startGroup] Example 1: Adding to Every Element :[font = text; inactive; preserveAspect] Let's add the integer 1 to each element of a vector, by individually assigning each entry. :[font = input; preserveAspect] addOne[v_List] := Module[{newv}, newv = v; Do[ newv[[i]] = v[[i]] + 1 ,{i,1,Length[v]}]; newv ] :[font = input; preserveAspect] addOne[{2,7,3,4,9}] :[font = text; inactive; preserveAspect] (Of course, this process is much simpler using Mathematica's list-based operations.) :[font = input; preserveAspect] {2,7,3,4,9}+1 :[font = text; inactive; preserveAspect; endGroup] Note that we had to copy vector "v" to "newv" in order to modify it; formal parameters such as "v" cannot be modified inside a function. :[font = subsection; inactive; Cclosed; preserveAspect; startGroup] Example 2: Cumulative Sums :[font = text; inactive; preserveAspect] A more interesting example is to take a vector and find the cumulative sum vector, made up of sums of consecutive elements beginning with the first element, so that "cumSum[ ]" of "{2,7,3,4,9}" would return "{2,9,12,16,25}". ;[s] 3:0,0;60,1;81,0;225,-1; 2:2,13,9,Times,0,12,0,0,0;1,13,9,Times,2,12,0,0,0; :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] Using Do and Assignment of Elements :[font = input; preserveAspect] cumSum[v_List] := Module[{sumv}, sumv = v; (* copy the vector *) Do[ (* successively add vector elements *) sumv[[i]] = sumv[[i-1]] + v[[i]] ,{i,2,Length[v],1}]; sumv (* return answer *) ] :[font = input; preserveAspect; endGroup] cumSum[{2,7,3,4,9}] :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] Using Do and Append :[font = input; preserveAspect] Clear[cumSum]; cumSum[v_List] := Module[{sumv}, sumv = {v[[1]]}; (* get the first entry *) Do[ sumv = Append[sumv, sumv[[i-1]] + v[[i]] ] ,{i,2,Length[v],1}]; sumv ] :[font = input; preserveAspect; endGroup] cumSum[{2,7,3,4,9}] :[font = text; inactive; preserveAspect; endGroup] One advantage of the first version of "cumSum[ ]" is that it does not use any list functions (such as "Append[ ]") except "Length[ ]". Many languages support arrays but no list functions; thus, the first version is programmable in almost any language. :[font = subsection; inactive; Cclosed; preserveAspect; startGroup] Example 3: Exchanging Two Rows :[font = text; inactive; preserveAspect] Suppose we have two matrices (two-dimensional arrays) which have the same number of columns, and we wish to exchange row "i" from the first matrix with row "j" of the second. Our strategy will be to copy row "i" from the first matrix to another temporary array, to copy row "j" from the second matrix to row "i" of the first, and finally to copy our temporary array to row "i" of the first. We will assume we are given two matrices as arguments together with "i" and "j"; we will return the two new matrices in a list. :[font = input; preserveAspect] Clear[rowSwitch] rowSwitch[m1_,m2_,i_,j_] := Module[ {newm1=m1,newm2=m2,temp,cols,k}, (* init newm1,2 *) If[Length[Dimensions[m1]]!=2|| Length[Dimensions[m2]]!=2, Print["Arrays not 2-dimensional"], If[Dimensions[m1][[2]]!=Dimensions[m2][[2]], Print["Number of columns different"], cols = Dimensions[m1][[2]]; (* find # cols *) temp = Table[0,{cols}]; (* initialize temp *) For[k=1, k<=cols, k++, (* k from 1 to cols *) temp[[k]]=newm1[[i,k]] ]; (* now temp has old row i *) For[k=1, k<=cols, k++, newm1[[i,k]] = newm2[[j,k]] ]; (* now row i is old row j *) For[k=1, k<=cols, k++, newm2[[j,k]] = temp[[k]] ]; (* now row j is temp *) {newm1,newm2} ] ] ]; :[font = input; preserveAspect] rowSwitch[twoD2,twoDArray,2,3] :[font = text; inactive; preserveAspect] Study the code carefully. Note again that local variables were used instead of formal parameters so that modifications could be made. Some will notice that a simpler solution exists, which we give below, which uses the fact that we can reference an entire row of a two-dimensional matrix by specifying only the row number between double brackets. For instance, "newm1[[i]]" refers to the entire list of row "i" in array "newm1". :[font = input; preserveAspect] Clear[rowSwitch2] rowSwitch2[m1_,m2_,i_,j_] := Module[ {newm1=m1,newm2=m2,temp,cols,k}, (* init newm1,2 *) If[Length[Dimensions[m1]]!=2|| Length[Dimensions[m2]]!=2, Print["Arrays not 2-dimensional"], If[Dimensions[m1][[2]]!=Dimensions[m2][[2]], Print["Number of columns different"], temp = newm1[[i]]; newm1[[i]] = newm2[[j]]; newm2[[j]] = temp; {newm1,newm2} ] ] ]; :[font = input; preserveAspect; endGroup; endGroup] rowSwitch2[twoD2,twoDArray,2,3] ^*)