我真的不明白如何通过感应对伪代码使用证明。它似乎与在数学方程上使用它的方式不同。
我正在尝试计算数组中可被k整除的整数数。
Algorithm: divisibleByK (a, k)
Input: array a of n size, number to be divisible by k
Output: number of numbers divisible by k
int count = 0;
for i <- 0 to n do
if (check(a[i],k) = true)
count = count + 1
return count;
Algorithm: Check (a[i], k)
Input: specific number in array a, number to be divisible by k
Output: boolean of true or false
if(a[i] % k == 0) then
return true;
else
return false;
如何证明这是正确的?感谢
答案 0 :(得分:15)
在这种情况下,我会将“归纳”解释为“对迭代次数的归纳”。
为此,我们首先建立一个所谓的loop-invariant。在这种情况下,循环不变量为:
count
存储可被k
整除且索引低于i
的数字。
此不变量保留循环条目,并确保循环后(i = n
)count
保存整个k可整除的值的数量>阵列。
归纳法看起来像这样:
基本情况:循环不变量保持循环输入(0次迭代后)
由于i
等于0,因此没有元素的索引低于i
。因此,索引小于i
的元素不能被k
整除。因此,由于count
等于0,因此不变量成立。
归纳假设:我们假设不变量保持在循环的顶部。
归纳步骤:我们显示不变量保存在循环体的底部。
执行正文后,i
已增加1。要在循环结束时保持循环不变量,必须相应地调整count
。
由于现在还有一个元素(a[i]
)的索引小于(新)i
,count
应该增加一个if(且仅当) a[i]
可以被k
整除,这正是if语句所确保的。
因此循环不变量在身体执行后也会保持。
QED。
在Hoare-logic中,它被证明(正式)像这样(为了清晰起见,将其重写为while循环):
{ I }
{ I[0 / i] }
i = 0
{ I }
while (i < n)
{ I ∧ i < n }
if (check(a[i], k) = true)
{ I[i + 1 / i] ∧ check(a[i], k) = true }
{ I[i + 1 / i][count + 1 / count] }
count = count + 1
{ I[i + 1 / i] }
{ I[i + 1 / i] }
i = i + 1
{ I }
{ I ∧ i ≮ n }
{ count = ∑ 0 x < n; 1 if a[x] ∣ k, 0 otherwise. }
I
(不变量)是:
count
=Σ x&lt;如果a[x]
| k ,我 1,否则为0。
(对于任何两个连续的断言行({...}
),我有一个证明义务(第一个断言必须暗示下一个断言),我作为练习留给读者; - )
答案 1 :(得分:2)
我们通过对n
的归纳证明了正确性,counter
是数组中元素的数量。你的范围是错误的,应该是0到n-1或1到n,但不是0到n。我们假设1到n。
在n = 0(基本情况)的情况下,我们只需手动完成算法。 i
以值0启动,循环不迭代,我们返回计数器的值,正如我们所说,它是0.这是正确的。
我们可以做第二个基础案例(尽管没有必要,就像常规数学一样)。 n = 1时。计数器初始化为0.循环进行一次传递,其中counter
取值为1,如果a
中的第一个值可被k
整除,则递增Check
(这是正确的,因为a[1]
算法的明显正确性。)
因此,如果k
无法被counter
整除,则返回0,否则我们返回1.此案例也可以解决。
感应很简单。我们假设n-1的正确性并且将证明为n(再次,就像在常规数学中一样)。为了正确形式,我们注意到counter
保存了在循环中最后一次迭代结束时返回的正确值。
根据我们的假设,我们知道在n-1次迭代后{{1}}保持关于数组中第一个n-1值的正确值。我们调用n = 1的基本情况来证明如果最后一个元素可以被k整除,它将为该值加1,因此最终值将是n的正确值。
QED。
您只需要知道执行导入的变量。通常输入大小最有帮助。此外,有时您需要假设所有自然数小于n的正确性,有时仅为n-1。再次,就像常规数学一样。