Коротко о сути: логика Хоара формализует доказательства корректности программ через тройки Хоара {P} C {Q}\{P\}\;C\;\{Q\}{P}C{Q}, где PPP — предусловие, CCC — команда, QQQ — постусловие. Правила: аксиома присвоения { P[E/x] } x:=E { P }\{\,P[E/x]\,\}\;x:=E\;\{\,P\,\}{P[E/x]}x:=E{P}, правил составления, следствия (уплотнение/ослабление), правило ветвления и циклa. Для циклов ключевая конструкция — инвариант III и правило (частичная корректность): если {I∧B} C {I}, то {I} while B do C {I∧¬B}.
\text{если }\{I\land B\}\;C\;\{I\}\text{, то }\{I\}\;\text{while }B\text{ do }C\;\{I\land\neg B\}. если{I∧B}C{I}, то{I}while B do C{I∧¬B}.
Для тотальной корректности дополнительно требуется убывающий вариант (меря), показывающий завершение. Пример (сумма первых nnn чисел): Программа i:=0;s:=0;while (i<n) { i:=i+1; s:=s+i }
i:=0;\quad s:=0;\quad \text{while }(i<n)\ \{\ i:=i+1;\ s:=s+i\ \} i:=0;s:=0;while (i<n){i:=i+1;s:=s+i}
Предполагаем предусловие n≥0 \,n\ge 0\,n≥0. Хотим получить постусловие s=n(n+1)2 \,s=\dfrac{n(n+1)}{2}\,s=2n(n+1). Выберем инвариант I:s=i(i+1)2 ∧ 0≤i≤n.
I:\quad s=\dfrac{i(i+1)}{2}\ \land\ 0\le i\le n. I:s=2i(i+1)∧0≤i≤n. Проверки: 1) Инициация (после инициализаций): после i:=0; s:=0i:=0;\ s:=0i:=0;s:=0 имеем i=0, s=0i=0,\ s=0i=0,s=0, значит s=0⋅(0+1)2 ∧ 0≤0≤n,
s=\dfrac{0\cdot(0+1)}{2}\ \land\ 0\le 0\le n, s=20⋅(0+1)∧0≤0≤n,
то есть III выполняется при предусловии n≥0n\ge 0n≥0. 2) Сохранение (тело цикла): предположим I∧i<nI\land i<nI∧i<n. После первой команды i:=i+1i:=i+1i:=i+1 новое значение iii равно старому iold+1i_{old}+1iold+1; запись инварианта сразу после первой команды даёт s=(i−1)i2 ∧ 1≤i≤n.
s=\dfrac{(i-1)i}{2}\ \land\ 1\le i\le n. s=2(i−1)i∧1≤i≤n.
Перед второй командой имеем это как предусловие для s:=s+is:=s+is:=s+i. По аксиоме присвоения, после s:=s+is:=s+is:=s+is=(i−1)i2+i=i(i+1)2,
s=\dfrac{(i-1)i}{2}+i=\dfrac{i(i+1)}{2}, s=2(i−1)i+i=2i(i+1),
и сохраняется 0≤i≤n0\le i\le n0≤i≤n. Следовательно III возвращается в конце тела. 3) Постусловие цикла: по правилу цикла из III и ¬(i<n)\neg(i<n)¬(i<n) получаем I∧¬(i<n) ⇒ s=i(i+1)2 ∧ i≥n.
I\land \neg(i<n)\ \Rightarrow\ s=\dfrac{i(i+1)}{2}\ \land\ i\ge n. I∧¬(i<n)⇒s=2i(i+1)∧i≥n.
В сочетании с частью i≤ni\le ni≤n инварианта даёт i=ni=ni=n и тогда s=n(n+1)2s=\dfrac{n(n+1)}{2}s=2n(n+1) — требуемое постусловие. (Для тотальной корректности добавляем меру n−in-in−i, которая неотрицательна и уменьшается на 111 при каждом проходе, следовательно цикл завершается.) Итог: доказательство корректности строится подбором инварианта III и проверкой инициации, сохранения и (при необходимости) убывающей меры для завершения.
если {I∧B} C {I}, то {I} while B do C {I∧¬B}. \text{если }\{I\land B\}\;C\;\{I\}\text{, то }\{I\}\;\text{while }B\text{ do }C\;\{I\land\neg B\}.
если {I∧B}C{I}, то {I}while B do C{I∧¬B}. Для тотальной корректности дополнительно требуется убывающий вариант (меря), показывающий завершение.
Пример (сумма первых nnn чисел):
Программа
i:=0;s:=0;while (i<n) { i:=i+1; s:=s+i } i:=0;\quad s:=0;\quad \text{while }(i<n)\ \{\ i:=i+1;\ s:=s+i\ \}
i:=0;s:=0;while (i<n) { i:=i+1; s:=s+i } Предполагаем предусловие n≥0 \,n\ge 0\,n≥0. Хотим получить постусловие s=n(n+1)2 \,s=\dfrac{n(n+1)}{2}\,s=2n(n+1) .
Выберем инвариант
I:s=i(i+1)2 ∧ 0≤i≤n. I:\quad s=\dfrac{i(i+1)}{2}\ \land\ 0\le i\le n.
I:s=2i(i+1) ∧ 0≤i≤n.
Проверки:
1) Инициация (после инициализаций): после i:=0; s:=0i:=0;\ s:=0i:=0; s:=0 имеем i=0, s=0i=0,\ s=0i=0, s=0, значит
s=0⋅(0+1)2 ∧ 0≤0≤n, s=\dfrac{0\cdot(0+1)}{2}\ \land\ 0\le 0\le n,
s=20⋅(0+1) ∧ 0≤0≤n, то есть III выполняется при предусловии n≥0n\ge 0n≥0.
2) Сохранение (тело цикла): предположим I∧i<nI\land i<nI∧i<n. После первой команды i:=i+1i:=i+1i:=i+1 новое значение iii равно старому iold+1i_{old}+1iold +1; запись инварианта сразу после первой команды даёт
s=(i−1)i2 ∧ 1≤i≤n. s=\dfrac{(i-1)i}{2}\ \land\ 1\le i\le n.
s=2(i−1)i ∧ 1≤i≤n. Перед второй командой имеем это как предусловие для s:=s+is:=s+is:=s+i. По аксиоме присвоения, после s:=s+is:=s+is:=s+i s=(i−1)i2+i=i(i+1)2, s=\dfrac{(i-1)i}{2}+i=\dfrac{i(i+1)}{2},
s=2(i−1)i +i=2i(i+1) , и сохраняется 0≤i≤n0\le i\le n0≤i≤n. Следовательно III возвращается в конце тела.
3) Постусловие цикла: по правилу цикла из III и ¬(i<n)\neg(i<n)¬(i<n) получаем
I∧¬(i<n) ⇒ s=i(i+1)2 ∧ i≥n. I\land \neg(i<n)\ \Rightarrow\ s=\dfrac{i(i+1)}{2}\ \land\ i\ge n.
I∧¬(i<n) ⇒ s=2i(i+1) ∧ i≥n. В сочетании с частью i≤ni\le ni≤n инварианта даёт i=ni=ni=n и тогда s=n(n+1)2s=\dfrac{n(n+1)}{2}s=2n(n+1) — требуемое постусловие.
(Для тотальной корректности добавляем меру n−in-in−i, которая неотрицательна и уменьшается на 111 при каждом проходе, следовательно цикл завершается.)
Итог: доказательство корректности строится подбором инварианта III и проверкой инициации, сохранения и (при необходимости) убывающей меры для завершения.