Numerical Computations - McMaster Computing and Software

EmilSekerinski,McMasterUniversity,WinterTerm16/17
COMPSCI1MD3IntroductiontoProgramming
"Thereareonly10typesofpeopleintheworld:
thosewhounderstandbinaryandthosewhodon't"
Integeraisanapproximatesquarerootofnif
a2 ≤ n < (a+1)2
Traceforinputn=27:
Onewaytocomputethesquarerootisby
linearsearch(exhaustivesearch):
Statement
a
A
0
def linearSqrt(n):
a = 0
A:
B
1
while (a+1)*(a+1) <= n:
B
2
B:
a = a+1
B
3
return a
>>> linearSqrt(27)
5
B
4
B
5
def linearSqrt (n):
a = 0
while (a+1)*(a+1) <= n:
a = a+1
A:
return a
Aisexecutedexactlyatimes,so:
linearSqrt(4) = 2
linearSqrt(8) = 2
linearSqrt(9) = 3
linearSqrt(10) = 3
→2times
→2times
→3times
→3times
Hence,Aisexecuted√ntimes(√hereintegersquareroot)
Supposewehavea,bsuchthattherootisbetweenaandb:
0 ≤ a < b and a2 ≤ n < b2
Werepeatedlyreplaceeitheraorbby(a+b)//2suchthatabove
holds,untila+1 == b;thenaistheapproximatesquareroot
Traceforn=27,a=0,b=8:
while a+1 != b:
Statement a
b
c
c = (a+b)//2
C:
D:
if c*c <= n: a = c
C
0
8
4
E:
else: b = c
D
4
8
4
D
4
8
6
E
4
6
6
Howdowefindsuitableinitial
C
4
6
5
valuesforaandb?
D
5
6
5
Forawetake0.Forb,thesmallestvaluesatisfying0 ≤ a < b
is1,whichwemultiplyby2untilbsatisfiesa2 ≤ n < b2
a,b:=0,1
def binarySqrt(n):
–
b*b≤n
a,
b
=
0,
1
+
b:=2*b
while b*b <= n:
b = 2*b
0≤a<b
a ≤n<b
–
while
a+1
!=
b:
a+1≠b
+
c = (a+b)//2
c:=(a+b)//2
if
c*c
<=
n:
a
=
c
–
+
c*c≤n
else: b = c
a:=c
b:=c
return
a
a+1=b
Howoftenaretheloopbodiesexecuted?
a ≤n<(a+1)
2
2
2
2
Thefirstloopsetsb = 2k > √nafterkexecutions.Thesecond
loophalvestheintervalb - aateveryexecution,untila+1 = b,
hencealsotakeskexecutions.
Traceforn=27:
def binarySqrt(n):
Statement a
b
c
A: a, b = 0, 1
while b*b <= n:
A
0
1
B:
b = 2*b
B
0
2
while a+1 != b:
B
0
4
C:
c = (a+b)//2
B
0
8
D:
if c*c <= n: a = c
C
0
8
4
else: b = c
E:
return a
…
…
…
…
Henceeachlooptakesk = log2 bexecutions,sok ≈ log2 √n
Atriple(a,b,c)ofintegersisPythagorean
ifa2+b2=c2
5
Asimplewaytofindsuchtriplesisbybrute
3
force:enumerateallvaluesofa,b,cand
checkiftheyformaPythagoreanTriple
4
def printPythagoreanTriples1(n):
for a in range(1, n+1):
for b in range(1, n+1):
for c in range(1, n+1):
if a**2+b**2 == c**2:
print(a, b, c)
printPythagoreanTriples1doesnotreturnaresult,but
hasaside-effect,printingonthescreen.Itisaprocedure
(method)ratherthanafunctioninthemathematicalsense
def printPythagoreanTriples1(n):
for a in range(1, n+1):
for b in range(1, n+1):
for c in range(1, n+1):
A:
if a*a+b*b == c*c:
print(a, b, c)
Eachofa,b,ctakendifferentvalues,inallcombinations,soA
isexecutedonn*n*n=n3combinationsintotal
Howcanweimprovethat?
Ratherthangoingoverallvaluesofc,wecalculatecfroma,b
andcheckifitisaninteger
def printPythagoreanTriples2(n):
for a in range(1, n+1):
for b in range(1, n+1):
A:
c2 = a*a+b*b
c = binarySqrt(c2)
if c <= n and c2 == c*c:
print(a, b, c)
Bothaandbtakendifferentvaluesinallcombinations,soAis
executedonn*n=n2combinationsintotal
Canweimprovefurther?
Both(3,4,5)and(4,3,5)areprinted,whichisunnecessary.
Wecanrestricta,b,csuchthat0<a≤b<c≤n
def printPythagoreanTriples(n):
for a in range(1, n):
for b in range(a, n):
A:
c2 = a*a+b*b
c = binarySqrt(c2)
if c <= n and c2 == c*c:
print(a, b, c)
Ifa=1,thenbtakesn-1values,ifa=2,thenn-2values,etc.
Intotal(n-1)+(n-2)+…+1=n(n-1)/2=n2/2-n/2
Comparedtothepreviousversion,Aisexecutedonlyhalfasoften
n3 n2 nlog2n
n
√n
log2n
n
Ifweknowhowmany
stepsaprogramtakes
dependingontheinput,
wecanusethistopredict
theexecutiontime
Thiscanbedonewithout
knowingdetailsofthe
processorandcompilation
tomachinelanguage!
Modificationwithoutprintingforbettermeasurement:
def countPythagoreanTriples1(n):
k = 0
for a in range(1, n+1):
for b in range(1, n+1):
for c in range(1, n+1):
A:
if a*a+b*b == c*c:
k = k+1
return k
Eachofa,b,ctakendifferentvalues,inallcombinations,soA
isexecutedonn*n*n=n3combinationsintotal
Forn=200:2003executionsofA;forn=400:4003executions
(4003/2003)=(400/200)3=8
Forn=400:8timeslonger,8tsec
Modificationwithoutprintingforbettermeasurement:
def countPythagoreanTriples2(n):
k = 0
for a in range(1, n+1):
for b in range(1, n+1):
A:
c2 = a*a+b*b
c = binarySqrt(c2)
if c <= n and c2 == c*c:
k = k+1
return k
Bothaandbtakendifferentvaluesinallcombinations,soAis
executedonn*n=n2combinationsintotal
Forn=200:2002executionsofA;forn=400:4002executions
Forn=400:4timeslonger,4tsec,whenignoringbinarySqrt
Modificationwithoutprintingforbettermeasurement:
def countPythagoreanTriples(n):
k = 0
for a in range(1, n):
for b in range(a, n):
A:
c2 = a*a+b*b
c = binarySqrt(c2)
if c <= n and c2 == c*c:
k = k+1
return k
Ifa=1,thenbtakesn-1values,ifa=2,thenn-2values,etc.
Intotal(n-1)+(n-2)+…+1=n(n-1)/2=n2/2-n/2
Forlargen,n/2isnegligible:((8002/2)/(4002/2))=4
Forn=800:4timeslonger,4tsec,whenignoringbinarySqrt
1/2+1/2==1.0
1/3+1/3+1/3==1.0
1/4+1/4+1/4+1/4==1.0
1/5+1/5+1/5+1/5+1/5==1.0
1/6+1/6+1/6+1/6+1/6+1/6==1.0
1/7+1/7+1/7+1/7+1/7+1/7+1/7==1.0
1/8+1/8+1/8+1/8+1/8+1/8+1/8+1/8==1.0
1/9+1/9+1/9+1/9+1/9+1/9+1/9+1/9+1/9==1.0
1/10+1/10+1/10+1/10+1/10+1/10+1/10+1/10+1/10+1/10==1.0
1.0+2.0==3.0
0.1+0.2==0.3
Thefixedpointrepresentationofafractionalnumberconsists
ofthesignificantdigitswithapointatafixedposition.For
decimalandbinarywith2integerand2fractionaldigits:
2
0.
5
3
1
0.
1
1
101 100 10-1 10-2
21
20
2-1
2-2
2*101+0*100+5*10-1+3*10-2 1*21+0*20+1*2-1+1*2-2
=2o+0+.5+.03
=2+0+.5+.25
=20.53
=2.75
Howmanybinarydigitsareneededtorepresent0.1?
. 0
0 0 1 1
=1/16+1/32=3/32=.09375
2-1 2-2 2-3 2-4 2-5
0 0 1 1 0 0 1 1 =51/512=.099609375
. 0
2-1 2-2 2-3 2-4 2-5 2-6 2-7 2-8 2-9
0
0
1
1
0
0
1
1
0
0
1
1
. 0
2-1 2-2 2-3 2-4 2-5 2-6 2-7 2-8 2-9 2-10 2-11 2-12 2-13
=819/8192=.0999755859375
Infinitelymanybinarydigitsareneeded:aftertheinitialdigit0,the
digits0011keeprepeating
WIKIPEDIA
Dependingonthebase,certainrationalnumberscannotbe
writtenwithfinitelymanydigits,e.g.1/3indecimal,1/10inbinary
.
?
?
?
?
?
2-1
2-2
2-3
2-4
…
.5
.25
.125
.0625
…
3/4=.11base2
è finitebinaryrepresentation
1/3=.01010101…base2 è nofinitebinaryrepresentation
FollowingtheIEEEstandard,Pythonuses52binarydigits,
approximately16decimaldigits.Standardoutputgivesonlythe
first16decimaldigits,eveniftheconversionfrombinaryresults
inmoredigits.Onlyanapproximatevalueisprinted!
>>> 1/3, 1/10
>>> format(1/3, ".60f"), format(1/10, ".60f")
.60f:printfloatingpointnumberwith60digitsafter.
Withafinitenumberofdigits,arithmeticoperationswillleadto
roundingerrors.Sometimetheerrorgetscancelled,sometimes
not
>>> 1/3+1/3+1/3, 1/6+1/6+1/6+1/6+1/6+1/6
>>> format(1/3+1/3+1/3, ".60f"), ...
Asaconsequence,fractionalnumbersshouldneverbe
comparedforequality:
a == b
shouldbecome
abs(b-a) ≤ ε
However,εmustnotbetoosmall!
def x_intersect(f, a, b, eps):
"f(a) ≤ 0 ≤ f(b), a ≤ b"
while b-a > eps:
m = (a+b)/2
if f(m) <= 0: a = m
else: b = m
return a, b
def f1(x):
return x*x-4
def f2(x):
return x*x-2
>>> type(f1)
InPython,functionsaredata;theycanbepassedaroundasanyotherdata
>>> x_intersect(f1, 0, 100, 1e-8)
>>> x_intersect(f2, 0, 100, 1e-8)
>>> x_intersect(f1, 0, 100, 1e-15)
>>> x_intersect(f1, 0, 100, 1e-16) # interrupt
Afloatingpointnumberconsistsofafraction(mantissa)with
thesignificantdigitsandanexponent.Forexample,fordecimal
numbers:
(1.963,3)=1.963*103=1963
FollowingtheIEEEstandard,Pythonstoresthefractionwith52
bitsinnormalizedform(leading1before.)andtheexponent
with11bits(withrange-1022to1023):
(1.f)*2e
Thelargestpositivenumberis
(1.11…base2)*21023=1.7976931348623157*10308
Thesmallestpositivenumberis
1.0*2-1022=2.2250738585072014*10-308
MostcomputersfollowtheIEEEstandard:floating-point
computationwillhavethesameresultsacrosscomputers.
Severalformatsexist,with8bytesbeingwidelyused:
52bitsfraction
1bitsign 11bitsexponent
Thenumberofbitsofthefractionlimitstheprecision.
Thenumberofbitsintheexponentlimitstherange.
Arithmeticoperationsonfloatmayleadtoalossof
significantdigits:
+,-onfloatarethe
>>> 10000000000000000+1
"dangerous"operations!
>>> 10000000000000000.0+1
−b± b2 −4ac
x 1/2 =
2a
def quadraticEquationSolution(a, b, c):
d = math.sqrt(b*b-4*a*c)
return (-b+d)/(2*a), (-b-d)/(2*a)
>>> quadraticEquationSolution(1, -3, -4)
>>> quadraticEquationSolution(1, -2e8, 1)
However,0.0isnotasolutionofx2-2e8*x+1=0:
!
b*b-4*a*c=(-2e8)*(-2e8)-4*1*1=4e16-4=4e16
lossofsignificantdigits
Therefore,d=sqrt(4e16)=2e8and:
(-b+d)/(2*a)=(-(-2e8)+2e8)/(2*1)=2e8
(-b-d)/(2*a)=(-(-2e8)-2e8)/(2*1)=0
Howcanweavoidsucherrors?
−b± b2 −4ac
x 1/2 =
2a
SolutionsofthequadraticequationarerelatedbyVieta'sformula:
c
WIKIPEDIA
x 1 x2 =
a
Giventhesolutionwithlargerabsolutevalue,thesmallercanbe
computedusingVieta,avoidingthelossofsignificantdigits
def quadraticEquationSolutionPlus(a, b, c):
d = math.sqrt(b*b-4*a*c)
x1 = -(b+d)/(2*a) if b>=0 else (d-b)/(2*a)
x2 = c/(x1*a)
return x1, x2
quadraticEquationSolutionPlus(1, -2e8, 1)
ArithmeticwiththePythondecimallibrarywillproducethesame
errorsascalculationsbyhand;bydefault,28fractionaldigitsare
kept;theprecisioncanbechanged:
>>> Decimal(1)/Decimal(3), Decimal(1)/Decimal(10)
>>> Decimal('0.1')+Decimal('0.2')==Decimal('0.3')
python.org
Howfastisdecimalarithmetic?
def timeDecimalAddition():
from time import time
start = time()
for i in range(100000):
x = Decimal(0)
for j in range(10):
x = x+Decimal(1)/Decimal(10)
return time()-start
ThePythonlibraryfractionsstoresrationalnumbersa/b
withnumeratoraanddenominatorbasapair(a, b).This
makescalculationswith+,-,*,/precise.
>>> Fraction(1)/Fraction(3) == Fraction(1, 3)
Conversionbetweenfloat,Decimal,Fractionreveals
differencesinrepresentation:
>>> Decimal(0.1)
>>> Fraction(0.1)
1. 
2. 
3. 
4. 
5. 
6. 
Avoidfloat,useintegerinstead:computewith¢
rather$,mmratherthanm
Usedecimalorfractionsstandardlibraryinstead:
slower,particularlyforverylarge/smallnumbers
Usealibraryforintervalarithmeticwithfloat:givessafe
lowerandupperbounds,takestwiceasmuchmemory/time
Useaproblem-specificlibrarywithorcheckliteraturefor
algorithmswithknownnumericalproperties(e.g.solving
differentialequations)
Usesymboliccomputationasincomputeralgebrasystems
insteadofaprogramminglanguage
Whenusingfloat,nevercompareforequality;check
plausibilityofresult