8Att bevisa egenskaper om program

Att bevisa
egenskaper
om program
8
■
Formella metoder............................................................... 1
Loop-program som statetransformers............................ 1
Några exempel ............................................................ 2
Partiell korrekthet och total korrekthet .......................... 3
Programspecifikation ................................................. 3
Hoarelogik och partiell korrekthet........................... 4
Valid eller ickevalid.................................................... 4
Två valida Hoareformler ........................................... 5
Några slutledningsregler för Hoareformler............ 5
Terminering ........................................................................ 7
Formella metoder
”Testing can reveal the presence of errors, not their absence.”
E.W. Djikstra
Betrakta följande problem.
Syntes Givet en programspecifikation, konstruera ett program som satisfier-
ar specifikationen.
Analys Givet ett program konstruera en programspecifikation som beskri-
ver programmet.
Verifiering Givet ett program och en programspecifikation visa att pro-
grammet satisfierar specifikationen.
Ekvivalens och optimering Givet ett program och en programspecifika-
tion konstruera ytterligare ett program som satisfierar samma specifikation,
men som är optimal i något avseende.
Korrigering (avlusning) Givet en programspecifikation och ett program
som inte satisfierar specifikationen korrigera programmet så att det satisfierar
specifikationen.
Samtliga dessa problem är välkända oavgörbara problem. Ändå forskas det
mycket på problemen ifråga. Skälet till detta är förstås att det inte bara finns
enstaka program utan hela klasser av program där problemen är avgörbara,
och där bra metoder – formella metoder – för att lösa problemen väntar på sin
lösning.
Dessa sidor utgör en liten introduktion till detta ämne för det fall att programmen är s.k. state transformers (Jag hittar inget bra svenskt namn.). Loopprogram (se kapitel 6) är en enkel klass av just state transformers.
■ Loop-program som statetransformers
Antag att ett visst loop-programs variabler är x 1, x 2, …, x n vilka var och en
är tillåtna att antaga godtyckliga värden i N . Det “utrymme” som programmet
kräver – man brukar tala om programmets tillståndsrum – kan då sägas vara
tillståndsrummet
N
×N ×…×N =
N
n
De värden som tupeln ( x 1, x 2, …, x n ) har i olika skeden av programkör-
1
2
Att bevisa egenskaper om program
ningen sägs vara programmets tillstånd.
Om vid en körning av programmet ifråga, det inträffar att programmet
stannar, och tupeln ( x 1, x 2, …, x n ) vid start respektive stopp har värdena
σ 0 = ( a 10, a 20, …, a n0 )
σ N = ( a 1N, a 2N, …, a nM )
sägs körningen ha σ 0 och σ N som start- respektive sluttillstånd.
Observera den
”omvända”
konventionen att
låta bågar
representera
tillstånd och noder
övergångar.
σ1
…
q0
qN
σN
En tillståndsövergång q k är här ett ”programsteg”, t ex
x 1 ← x 2 , x 1 ← 0 eller x 1 ++ .
Några enkla exempel illustrerar de just införda begreppen:
■ Några exempel
EXEMPEL 1
x –– =
Betrakta det primitiva loopprogram som minskar en variaz←x
bels värde med en enhet. Proy←0
gramvariablerna är x, y, z , så
z ggr { x ← y
programmets tillståndsrum är
y++ }
3
N .
En körning där x har värdet 2 vid start ger upphov till följande sekvens av
tillståndsövergångar:
( 2, –, – )
( 0, 1, 2 )
EXEMPEL 2
z←x
x←y
( 2, –, 2 )
y←0
( 1, 1, 2 )
y++
( 2, 0, 2 )
x←y
( 0, 0, 2 )
y++
( 1, 2, 2 )
EUKLIDES ALGORITM för beräkning av största gemensamma delare till två
positiva naturliga tal kan formuleras som nedan till vänster. Loop-programmet
till höger med tre programvariabler, dvs med tillståndsrum N 3 , implementerar algoritmen.
SGD(x, x) = x
SGD(x, y) = SGD(y, x) om x < y
SGD(x, y) = SGD(y, x – y) om x > y
Sgd x och y i d =
Sålänge ( x ≠ y )
{
Om ( x > y )
så ( x ← x – y )
annars ( y ← y – x )
}
d←x
Här är ett körexempel av loop-programmet:
( 6, 8, – )
y←y–x
( 2, 2, – )
d←x
( 6, 2, – )
( 2, 2, 2 )
x←x–y
( 4, 2, – )
x←x–y
Partiell korrekthet och total korrekthet
■
3
Partiell korrekthet och total korrekthet
Vi skall intressera oss för två aspekter på ett program. Den ena behandlar frågan om programmets output är önskat output (givet av en programspecifikation). Den andra aspekten behandlar även frågan om programmet ifråga
stannar.
1
Ett program sägs vara partiellt korrekt om alla dess output är önskade output.
2
Ett program sägs vara totalt korrekt om det stannar (terminerar) för varje
input och om dess output är önskade output.
En seriös diskussion kräver formalisering. Därför ska vi införa en formell apparat för programspecifikation, dvs för beskrivning av input och önskade
output.
EXEMPEL 3
Ett program för beräkning av SGD (som i EXEMPEL 2) till två positiva naturliga tal X, Y skall förslagsvis ha två inputvariabler, säg x, y med initialvärden
X, Y , samt en outputvariabel säg d med värdet SGD(X, Y) när programmet
stannar.
Dvs givet ( ( x = X > 0 ) ∧ ( y = Y > 0 ) ) vid start, önskas d = SGD(X, Y) vid
stopp. Tillsammans utgör dessa två villkor en programspecifikation. Filosofin
bakom detta synsätt är att programkonstruktören skall vara fri att utforma
programmet efter eget huvud, bara input och output stämmer med de specificerade.
■ Programspecifikation
En s.k. programspecifikation
φ = ( previllkor, postvillkor )
är ett par av logiska utsagor beskrivande programmets start- och stopptillstånd, närmare bestämt programmets inputvariabler vid start och dess
outputvariabler vid stopp. I första exemplet nedan ges specifikationer φ beskrivande några mycket enkla program P. (Jfr avsnittet Syntes sid 1.)
EXEMPEL 4
De enklaste loop-programmen och specifikationer av dem:
program P
x←0
x++
x ggr { }
EXEMPEL 5
specifikation φ
( ( x ≥ 0 ), ( x = 0 ) )
( ( x = X ≥ 0 ), ( x = X + 1 ) )
( ( x = X ≥ 0 ), ( x = X ) )
I detta exempel presenteras en specifikation φ men inget program. Avsikten är
att ge en formell beskrivning av input och output i ett program för division
mellan ett naturligt tal X och ett positivt naturligt tal Y. (q och r är outputvariabler vars värden vid stopp är naturliga tal representerande kvoten respektive
resten, x och y är inputvariabler.)
φ = ( ( ( x ≥ 0 ) ∧ ( y ≥ 0 ) ), ( ( x = qy + r ) ∧ ( r ≥ 0 ) ∧ ( r < y ) ) )
ÖVNING 1 Konstruera ett loop-program som satisfierar φ i EXEMPEL 5.
4
Att bevisa egenskaper om program
■ Hoarelogik och partiell korrekthet
Antag att φ = ( p, q ) är en programspecifikation och att Du har konstruerat
ett program P avsett att satisfiera φ.
Om för de igångkörningar av ditt program P som stannar, det gäller att
Om p är sann innan P körs igång,
så är q sann efter att P har stannat
(1)
så har Du gjort ett gott jobb. (Det enda man skulle kunna begära ytterligare är att få en garanti på att igångkörda P alltid stannar.) Man säger (om (1)
stämmer) att
P är partiellt korrekt m.a.p. φ = ( p, q )
Notera att (1) är en logisk utsaga som kan vara sann eller falsk. Den är bildad
med hjälp av utsagorna p, q samt programmet P. Därför betecknas den med
formeln
{ p }P { q }
Hoareformel
En sådan formel kallas Hoareformel (efter C.A.R. Hoare)
Begreppet partiell korrekthet kan alltså definieras på följande sätt:
DEFINITION P är partiellt korrekt m.a.p. en specifikation φ = ( p, q ) om { p }P { q }
är sann för varje körning.
■ Valid eller ickevalid
En Hoareformel { p }P { q } som är sann vid varje körning av P sägs vara valid,
annars ickevalid. Villkoret om partiell korrekthet för ett program P visavi en
programspecifikation ( p, q ) kan således uttryckas som att { p }P { q } är valid.
Och innebörden i “validitet” resp. dess motsats “ickevaliditet” är som
nedan:
ickevalid
@
{ p }P { q } är ickevalid om vid någon körning av P det gäller att
1 p är sann innan P körs, det är sant att P stannar och q är falsk när detta inträffar.
valid
@
{ p }P { q } är valid om för varje körning av P någon av följande tre punkter gäller
p är sann innan P körs, det är sant att P stannar, och q är sann när detta inträffar
3 p är sann innan P körs, och det är falskt att P stannar
4 p är falsk innan P körs
2
EXEMPEL 6
För de enklaste loop-programmen
x ← 0,
x++,
x ggr { }
är följande tre formler valida
{ x ≥ 0 }x ← 0 { x = 0 }
{ x = X ≥ 0 }x++ { x = X + 1 }
{ x = X ≥ 0 } ( x ggr { } ) { x = X }
Partiell korrekthet och total korrekthet
5
Däremot är t ex formeln
{ x = X ≥ 0 }x++ { x = 1 }
ickevalid. Om x ← 0 startas med t ex x = 1 gäller ju punkt 1 ovan.
■ Två valida Hoareformler
{ p }P { sann }
(1)
{ falsk }P { q }
(2)
(1) är valid (oberoende av p och P) eftersom postvillkoret i detta fall alltid är
sant. (En ickevalid formel förutsätter att postvillkoret är falskt efter någon
körning av P (se punkt 1 ).)
(2) är valid (oberoende av q och P) eftersom previllkoret i detta fall alltid är
falskt. (En ickevalid formel förutsätter att previllkoret är sant innan någon
körning av P (se punkt 1 ).)
■ Några slutledningsregler för Hoareformler
S.k. slutledningsregler (eller inferensregler) som vanligen skrivs på följande
sätt
u
--v
och
u 1, u 2
- - -- - - - - - - - - - - v
uttrycker att man kan sluta sig till att v är valid om man vet att u är valid
(respektive om man vet att u 1 och u 2 båda är valida). Som Du ser används
kommatecken istället för “och”. Tecknet ∧ reserverar vi för användning inuti
pre- och post-villkoren.
p → p', { p' }P { q }
------------------------------------------{ p }P { q }
(3)
{ p }S { q' }, q' → q
------------------------------------------{ p }P { q }
(4)
Och-regeln
{ p }P { q' }, { p }P { q'' }
----------------------------------------------------{ p }P { q' ∧ q'' }
(5)
Eller-regeln
{ p' }P { q }, { p'' }P { q }
----------------------------------------------------{ p' ∨ p'' }P { q }
(6)
Sekvensregeln
{ p }P { q }, { q }P' { r }
-------------------------------------------------{ p }PP' { r }
(7)
Iterationsreglerna
{ p }P { p }
-----------------------------------------------------{ p } ( x ggr { P } ) { p }
(8)
Implikationsreglerna
Med PP' menas
”kör P och
sedan P' ”
ovillkorlig
(primitiv) loop
villkorlig loop
{ p ∧ ( x ≠ 0 ) }P { p }
------------------------------------------------------------------------------------------------------------{ p } ( Sålänge ( x ≠ 0 ) { P } ) { p ∧ ( x = 0 ) }
(9)
Vi visar (med ett motsägelseresonemang) att den första implikationsregeln
(3) är korrekt.
Antag tvärtom att p → p' och { p' }P { q } är valida men att { p }P { q } är
6
Att bevisa egenskaper om program
ickevalid. Då gäller att
q är falskt
(10)
efter någon körning av P där p var sann innan P startades. Men p → p' tillsammans med att p är sann, ger att p' är sann, och validiteten hos { p' }P { q }
ger då att q är sann efter körningen ifråga, vilket motsäger (10). Av
motsägelsen följer att implikationsregeln måste vara korrekt.
#
På motsvarande sätt kan de övriga reglerna visas vara korrekta.
EXEMPEL 7
Betrakta divisionsprogrammet nedan:
Dividera x med y i q och r =
q ← 0
r ← x
Sålänge r≥y {
Subtrahera y från r
q++}
Vi bevisar att divisionsprogrammet är partiell korrekt m.a.p. specifikationen
φ = ( ( ( x ≥ 0 ) ∧ ( y > 0 ) ), ( ( x = qy + r ) ∧ ( r ≥ 0 ) ∧ ( r < y ) ) )
dvs att
{ ( x ≥ 0 ) ∧ ( y > 0 ) }Dividera x med y i q och r { ( ( x = qy + r ) ∧ ( r < y ) ) }
är sann för varje körning.
BEVIS
aritmetik
( ( x ≥ 0 ) ∧ ( y > 0 ) ) → ( ( x = 0y + x ) ∧ ( x ≥ 0 ) )
{ ( x = 0y + x ) ∧ ( x ≥ 0 ) } ( q ← 0 ) { ( x = qy + x ) ∧ ( x ≥ 0 ) }
{ ( x = qy + x ) ∧ ( x ≥ 0 ) } ( r ← x ) { ( x = qy + r ) ∧ ( r ≥ 0 ) }
sekvens
{ ( x = 0y + x ) ∧ ( x ≥ 0 ) } ( q ← 0 ) ( r ← x ) { ( x = qy + r ) ∧ ( r ≥ 0 ) }
aritmetik
( ( x = qy + r ) ∧ ( r ≥ 0 ) ∧ ( r ≥ y ) ) → ( ( x = ( q + 1 )y + ( r – y ) ) ∧ ( r ≥ y ) )
{ ( x = ( q + 1 )y + ( r – y ) ) ∧ ( r ≥ y ) }Subtrahera y från r { ( x = ( q + 1 )y + r ) ∧ ( r ≥ 0 ) }
{ ( x = ( q + 1 )y + ( r – y ) ) ∧ ( r ≥ 0 ) }q++ { ( x = qy + r ) ∧ ( r ≥ 0 ) }
p är en invariant
iteration
EXEMPEL 8
P = ( Subtrahera y från r ) ( q++ ) och p = ( x = qy + r ) ∧ ( r ≥ 0 )
{ p }P { p }
-------------------------------------------------------------------------------------------------{ p } ( Sålänge ( r ≥ y ) { P } ) { p ∧ ( r < y ) }
Betrakta specifikationen φ = ( n ≥ 0, r 2 ≤ n < ( r + 1 ) 2 ) tillsammans med följande loop-program.
Kvadratrot n i r =
r ← 0
k ← 0
Sålänge k≤n {
r++
k ← r.r}
r––
Terminering
7
Vi bevisar partiell korrekthet genom att (som i förra exemplet) ta ett programsteg i taget, och visa att följande Hoareformel är valid.
{ n ≥ 0 } Kvadratrot n i r { r 2 ≤ n < ( r + 1 ) 2 }
BEVIS
{ n ≥ 0 } r ← 0 { ( 0 = r2 ) ∧ ( 0 ≤ n ) }
{ ( 0 = r2 ) ∧ ( 0 ≤ n ) } k ← 0 { ( k = r2 ) ∧ ( k ≤ n ) }
{ ( k = r 2 ) ∧ ( k ≤ n ) } r++ { ( k = ( r – 1 ) 2 ) ∧ ( ( r – 1 ) 2 ≤ n ) }
(11)
{ ( k = ( r – 1 )2 ) ∧ ( ( r – 1 )2 ≤ n ) } k ← r ⋅ r { ( k = r2 ) ∧ ( ( r – 1 )2 ≤ n ) }
(12)
Låt P = ( r++ ) ( k ← r ⋅ r ) . Då följer av (11) och (12) att
{ ( k = r2 ) ∧ ( k ≤ n ) } P { ( k = r2 ) ∧ ( ( r – 1 )2 ≤ n ) }
(13)
Av (13) och iteration följer i sin tur
{ ( k = r2 ) ∧ ( k ≤ n ) } P { ( k = r2 ) ∧ ( ( r – 1 )2 ≤ n ) }
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------2
{ ( k = r ) } Sålänge k ≤ n { P } { ( k = r 2 ) ∧ ( ( r – 1 ) 2 ≤ n ) ∧ ( k > n ) }
Och av aritmetik:
( ( k = r2 ) ∧ ( ( r – 1 )2 ≤ n ) ∧ ( k > n ) ) → ( ( ( r – 1 )2 ≤ n ) ∧ ( r2 > n ) )
Till sist: { ( ( r – 1 ) 2 ≤ n ) ∧ ( r 2 > n ) } r– – { ( r 2 ≤ n ) ∧ ( ( r + 1 ) 2 > n ) }
Aritmetik: ( ( ( r – 1 ) 2 ≤ n ) ∧ ( r 2 > n ) ) → ( r 2 ≤ n < ( r + 1 ) 2 ) .
■
Terminering
En ideal lösning till en given programspecifikation φ = ( p, q ) är ett program
P sådant att
P är partiellt korrekt m.a.p. φ = ( p, q ) , och
P stannar vid varje körning där p är sann innan P startas.
(14)
Man säger att P i detta fall är totalt korrekt m.a.p. φ = ( p, q ) .
Eftersom de enda loop-program som överhuvudtaget har någon möjlighet
att efter igångkörning vägra att stanna är de som innehåller en eller flera villkorliga loopar, så nöjer vi oss med att diskutera terminering för programavsnitt av typ
Sålänge x≠0 {B}
(15)
Sålänge villkor {B}
(16)
eller (allmännare)
En garanti för att (15) stannar är att variabeln x minskar med en eller flera enheter vid varje genomgång av repetitionsblocket B. Ty då kommer villkoret
“x≠0” (som är ekvivalent med villkoret “x>0” för naturliga tal x) förr eller senare att bli falskt.
På motsvarande sätt kan man slå fast att (16) stannar, om man lyckas hitta
en kvantitet z sådan att
1 z minskar vid varje genomgång av repetitionsblocket B,
8
Att bevisa egenskaper om program
2
z≥z0 för något z0, (OBSERVERA att detta villkor är uppfyllt för varje z vars
värden är naturliga tal.)
eller sådan att
3 z ökar vid varje genomgång av repetitionsblocket B,
4 z≤z0 för något z0.
EXEMPEL 9
I programavsnittet nedan (som är hämtat från EXEMPEL 8) uppfylls stoppvillkoren 3 och 4 av variabeln k, vilket garanterar terminering.
Sålänge k≤n
{
r++
k ← r.r
}
EXEMPEL 10
Vad sägs om följande programavsnitt då?
Sålänge x är jämn
{
x ← x/2
}
Stoppvillkoret 2 är uppfyllt av variabeln x (ja av varje variabel!) Detta gäller
oavsett vilket värde som x har vid start.
Villkoret 1 däremot uppfylls enbart om x är positiv vid igångkörningen.
(Om x har värdet 0 - ett jämnt tal - vid start, så kommer x efter varje genomkörning av repetitionsblocket att ha värdet 0. Dvs då minskar inte x vid genomkörning av repetitionsblocket.)
Slutsatsen är att programavsnittet ifråga stannar om och endast om det
startas med ett positivt x.
EXEMPEL 11
Nedanstående loop är tagen från EXEMPEL 2.
Sålänge x≠y
{
Om x>y så (x ← x-y) annars (y ← y-x)
}
Här är det inte lika uppenbart som i de förra exemplen hur man kan hitta en
stoppvillkorsuppfyllande kvantitet.
Men betrakta x+y. Eftersom vid varje besök i repetitionsblocket den ena
av x,y minskar medan den andra lämnas oförändrad, kommer x+y att minska vid varje sådant besök. Därmed är stoppvillkoret 1 uppfyllt av x+y. Och
villkoret 2 är ju alltid uppfyllt (av kvantiteter vars värden är naturliga tal). I
detta fall är villkoret 2 t.o.m. uppfyllt genom programavsnittets egen försorg.
Ty tack vare villkoret “x>y” i repetitionsblocket kommer x enbart att kunna
tilldelas positiva värden av “x ← x-y”. Och på motsvarande sätt kommer y
enbart att kunna tilldelas ickenegativa värden. Således kommer x+y enbart
att kunna tilldelas positiva värden.
Slutsats: slingan stannar (ifall den körs igång).