Tricks of The Trade: Tutorial
Tricks of The Trade: Tutorial
This is a column of programming tricks and techniques, most of which, we hope, will be
contributed by our readers. You are encouraged to submit ideas to this column.
1 + 3 + 5 + L (2n 1) = n2
VOLUME 5, ISSUE 4 27
Here are the first 15 Fibonacci numbers: Sound “Visualization”
In[4]:= Array[fib, 15] Every user should be aware that Mathematica has excellent
graphics capabilities which greatly assist visualization. How-
Out[4]= {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610}
ever, although Mathematica includes sound capabilities, per-
haps not too many users have taken advantage of them for
A visual proof, due to Alfred Brousseau, involves con-
visualization. Here are a few examples that illustrate the use
structing a graphic that draws and places boxes with sizes
of ListPlay, and show how to use Compile to speed up opera-
equal to the squares of successive Fibonacci numbers
tions that involve lists.
In[5]:= rect[{a_, b_}, {x_, y_}] :=
Logistic Map
{{x, y}, {a+x, y}, {a+x, b+y}, {x, b+y}, {x, y}}
The logistic map is the mapping x a mx(1 - x) where 0 £ m
In[6]:= boxes[n_] := Table[Line[rect[{fib[i], fib[i]}, £ 4 and 0 £ x £ 1 (see, for example, Roman Maeder’s article
If[EvenQ[i], {fib[i-1], 0}, {0, fib[i-1]}]]], “Function Iteration and Chaos” in the Journal 5(2): 28–40).
{i, n}] An efficient iterative implementation to help visualize the
behavior as m = 3 + t/n varies from 3 to 4 is
In[7]:= Show[Graphics[boxes[5]]]
In[1]:= logistic[n_Integer, start_:2/3] :=
Module[ {f, t, x},
f = Compile[{x, t},
(3 + t/n) x (1 - x)];
FoldList[f, N[start], Range[0, n]] ]
In[8]:= grid[n_] :=
Module[{k, l},
If[OddQ[n], (k=0; l=1), (k=1; l=0)];
{Table[Line[{{0, j}, {fib[n+k], j}}],
Playing this sound one hears the period-doubling route to
{j, 0, fib[n+l]}],
chaos.
Table[Line[{{i, 0}, {i, fib[n+l]}}],
It may not be immediately obvious that f in this Module
{i, 0, fib[n+k]}]}]
will be fully compiled. One way to check is to use With to
In[9]:= Show[Graphics[{{GrayLevel[0.8], grid[7]}, boxes[7]}]] provide a suitable (integer) value for the parameter n, and
then project out the heart of the InputForm of the compiled
function (which is part [[1,3]]):
VOLUME 5, ISSUE 4 29
In[11]:= ListPlay[First[RealDigits[N[Sqrt[2], 500]]]] BinarySearch[l_List, k_Integer,
low_Integer, high_Integer, f_] :=
Module[{mid = Floor[ (low + high)/2 ]},
If [low > high, Return[low - 1/2]];
If [f[ l[[mid]] ] == k, Return[mid]];
If [f[ l[[mid]] ] > k,
BinarySearch[l, k, 1, mid-1, f],
BinarySearch[l, k, mid+1, high, f] ] ]
This error makes the routine run much slower and causes it
to use a great deal more memory but, as the routine works,
as do rational numbers with digit sequences longer than the
no one would notice much difference when searching short
sample range:
lists. Here are some test results of the corrected routine run
In[12]:= Short[N[1/499, 505], 2] on an IBM PC 486/66 with 8 MB of RAM:
Out[12]//Short=
No. of list elements Best times (seconds)
0.002004008016032064128256513026052104208416833667334669\ 10000 1.373
<<423>>539078156312625250501002004008 20000 2.691
In[13]:= ListPlay[First[RealDigits[%]]] 50000 9.173
100000 63.933
200000 out of memory
400000 out of memory
BinarySearch[l_List, k_Integer,
low_Integer, high_Integer, f_]:=
Module[{lo, mid, hi, el},
lo = low;
hi = high;
While[lo <= hi,
Binary Searching If[(el = f[l[[mid = Floor[(lo + hi)/2]]]]) == k,
Geza Makay Return[mid]];
[email protected] If[el > k, hi = mid-1, lo = mid+1] ];
Return[lo - 1/2] ]
The binary search is the fastest way to find an element in an
ordered list. The standard package DiscreteMath`Combinator-
ica` contains an implementation, Since computing f (the function that extracts the key from
an element of the list) twice may take a long time, we calcu-
In[1]:= Needs[“DiscreteMath`Combinatorica`”] late it once and save the result for later use. The improve-
ment in speed is quite impressive:
In[2]:= ?BinarySearch
BinarySearch[l,k,f] searches sorted list l for key k and No. of list elements Best times (seconds)
returns the position of l containing k, with f a 10000 0.124
function which extracts the key from an element of l. 20000 0.206
50000 0.417
but it turns out to be too slow for long lists. Looking at the 100000 0.928
source code of this routine reveals a misprint: 200000 107.5
400000 out of memory
Note that this routine gives run-times depending logarith- In[1]:= Split[{a_, b___}, test_:SameQ] := Split[{{a}, {b}}, test]
mically on the length of the list only if the list is passed as a
variable in the first argument. Otherwise, the assignment of The matching condition applies the test to the last element of
the local variable ls=l evaluates the expression l, which gives the first list and the first element of the last list and proceeds
run-times linear in the length of the list. recursively:
To test these routines, we used a randomly generated
sorted list of integers, In[2]:= Split[{{a___, b_}, {c_, d___}}, t_] /; t[b, c] :=
Split[{{a, b, c}, {d}}, t]
In[6]:= n = 50000;
In[3]:= Split[{{a___, b_}, {c_, d___}}, t_] :=
In[7]:= alist = NestList[# + Random[Integer, 100]&, 0, n]; {{a, b}} ~Join~ Split[{c, d}, t]
and a random number to search for: Note that conditional tests can appear on the left-hand side
of an expression.
In[8]:= rand := Random[Integer, Last[alist]]
The recursion terminates when the last element is an
In[9]:= BinarySearch[alist, rand, 1, n, Identity] // Timing empty list:
15
Out[9]= {0.25 Second, —} In[4]:= Split[{a_, {}}, t_] := {a}
2
Here are two examples. The first uses the default (SameQ)
test:
VOLUME 5, ISSUE 4 31
In[5]:= Split[{a, a, a, a, b, b, c, c, c, c, d}] In[6]:= Catch[Fold[
Out[5]= {{a, a, a, a}, {b, b}, {c, c, c, c}, {d}} If[#1 > 6.0, Throw[#2 - 1], #1 + 1/#2 ]&,
0.0, Range[300]]]
A second application partitions a list of random numbers, Out[6]= 227
In[7]:= Split[%, Abs[#1 - #2] <= 0.4 &] In[8]:= % - 1/%% < 6.0 < %
Out[7]= {{0.515833}, {0.107546, 0.114147}, {0.682227, 0.304036}, Out[8]= True
{0.905033}, {0.380635, 0.044102, 0.360654}, {0.845311}}
Note that when using Nest or Fold one needs to choose suf-
ficiently large iteration parameters to ensure that a solution is
Catch and Throw encountered before the iteration terminates.
Instead of using Do, For, or While for repetitive computation
involving testing, one can use Nest, Fold, or FixedPoint along Vector and Matrix Norms
with Catch and Throw. Here are four examples. Mathematica does not include any of the standard vector or
The first power of 2 that is greater than 109: matrix norms in the kernel or the standard packages. Per-
haps one reason for this is that any specific norm is very
In[1]:= Catch[ easy to construct using built-in list or matrix operations – in
FixedPoint[If[# > 10^9, Throw[#], 2 #]&, 2]] fact all the definitions below are “one-liners.” Section 16.8.1
Out[1]= 1073741824 of the Handbook of Applied Mathematics, edited by Carl E.
Pearson (Van Nostrand Reinhold, 1990), lists a number of
As a check: vector and matrix norms.
The following vector and matrix will be used in the
In[2]:= %/2 < 10^9 < % numerical examples:
Out[2]= True
In[1]:= vec = Table[Random[], {5}]
The smallest positive integer n such that n! is greater than Out[1]= {0.650118, 0.653373, 0.522767, 0.464846, 0.484166}
108: In[2]:= (mat = Table[Random[], {2}, {2}]) // MatrixForm
Out[2]//MatrixForm=
In[3]:= Catch[Fold[
If[#1 > 10^8, Throw[#2 - 1], Times[##]]&, 0.832695 0.0522864
1, Range[20]]] 0.206315 0.644941
Out[3]= 12
Vector norms
As a check: The Hölder norm is defined by
In[4]:= 11! < 10^8 < 12! Ê n ˆ1 p
Out[4]= True Á
Á
Ë
Âi =1
p
xi ˜ , p ≥ 1
˜
¯
The first arithmetic sum (1 + 2 + 3 + L + n) that exceeds
and can be implemented directly as
10000:
In[3]:= HolderNorm[x_?VectorQ, p_ /; p >= 1] :=
In[5]:= Catch[Fold[
(Plus @@ Flatten[Abs[x]^p])^(1/p)
If[#1 > 10000, Throw[#1], Plus[##]]&,
0, Range[200]]]
For example,
Out[5]= 10011
In[4]:= HolderNorm[vec, 3]
The number of terms n in the harmonic sum (1 + 1ˇ2 +
Out[4]= 0.969231
1ˇ3 + L + 1ˇn) required to exceed 6.0:
1.5
The maximum norm of an n ¥ n matrix a is simply the
maximum absolute value occuring in the matrix multiplied
2 4 6 8 10 by n:
In[16]:= MaximumNorm[a_?MatrixQ] :=
The e-norm is simply the maximum absolute value of the Length[a] Max[Abs[a]] /; SquareQ[a]
elements of the vector x:
In[17]:= MaximumNorm[mat]
In[6]:= eNorm[x_?VectorQ] := Max[Abs[x]] Out[17]= 1.66539
In[7]:= eNorm[vec]
The definition of the Hölder matrix norm is identical to
Out[7]= 0.653373
the vector case except for the extra restriction on p:
and may be interpreted as a special case of the Hölder norm Ê ˆ1 p
p˜
for p Æ •: Á
ÁÁ
Ë
Âa
i, j
ij ˜˜
¯
, 1£ p £ 2
In[8]:= HolderNorm[vec, 1000]
Out[8]= 0.65648 The implementation is direct:
1.6
The e¢-norm is a special case of the Hölder norm with
p = 1: 1.5
1.4
In[11]:= HolderNorm[vec, 1] 1.3
Out[11]= 2.77527 1.2
The Euclidean norm is a special case of the Hölder norm 1.2 1.4 1.6 1.8 2
with p = 2:
The Euclidean norm is a special case of the Hölder norm
n with p = 2,
2
Âx i
2
i =1
Âa i, j
ij
VOLUME 5, ISSUE 4 33
In[21]:= EuclideanNorm[a_?MatrixQ] := HolderNorm[a, 2] In[26]:= ?SingularValues
where † indicates the conjugate transpose and the trace of a In[27]:= SpectrumNorm[a_?MatrixQ] :=
matrix, tr, is the sum of its diagonal elements: Max[SingularValues[a][[2]]] /; SquareQ[a]
In[28]:= SpectrumNorm[mat]
In[23]:= tr[a_?MatrixQ] := Plus @@ Transpose[a, {1, 1}]
Out[28]= 0.902607
In[24]:= (tr[Conjugate[Transpose[mat]] . mat])^(1/2)
Out[24]= 1.07454 Condition numbers
For a square matrix A, the condition number Cf(A) is defined
The spectrum norm is the largest singular value of the by f(A)f(A–1), where f is a specified norm. This number
matrix. The singular values of a matrix are defined by gives a bound on the sensitivity of changes in the solution x
of the equation Ax = b, with respect to changes in b.
In[25]:= Sqrt[Eigenvalues[Conjugate[Transpose[mat]] . mat]]
From the definition, here is an implementation that uses
Out[25]= {0.902607, 0.583035} the Euclidean norm by default:
In[30]:= ConditionNumber[mat]
Out[30]= 2.19406
1 1
- -
1 2 3
1 1 1
- - -
2 3 4
1 1 1
- - -
3 4 5
In[36]:= ConditionNumber[N[mat]]
Out[36]= 526.159