某些算法(分配二叉树......)需要计算基数2指数。如何为这种原生类型计算它?
function pow2(n: u32): (r: u32)
requires n < 10
{
if n == 0 then 1 else 2 * pow2(n - 1)
}
这是一个明显的尝试:
u32
它失败了,因为Dafny怀疑产品保持低于{{1}}的最大值。如何证明它的价值低于2 ** 10?
答案 0 :(得分:1)
在这种情况下,首先定义函数的无界版本更方便,然后证明一个引理,表明当n < 10
(或n < 32
,偶数)它在边界内时。
function pow2(n: nat): int
{
if n == 0 then 1 else 2 * pow2(n - 1)
}
lemma pow2Bounds(n: nat)
requires n < 32
ensures 0 <= pow2(n) < 0x100000000
{ /* omitted here; two proofs given below */ }
function pow2u32(n: u32): u32
requires n < 32
{
pow2Bounds(n as nat);
pow2(n as nat) as u32
}
直观地说,我们可能会期望引理自动完成,因为只有少数案例需要考虑:n = 0
,n = 1
,... n = 31
。但是Dafny不会自动执行此类案例分析。相反,我们有几个选择。
首先,我们可以证明一个更普遍的属性,通过归纳推理的魔力,可以更容易来证明,尽管它比我们需要的更强。
lemma pow2Monotone(a: nat, b: nat)
requires a < b
ensures pow2(a) < pow2(b)
{} // Dafny is able to prove this automatically by induction.
然后是引理。
lemma pow2Bounds(n: nat)
requires n < 32
ensures 0 <= pow2(n) < 0x100000000
{
pow2Monotone(n, 32);
}
证明这一点的另一种方法是告诉Dafny它应该使用pow2
属性将:fuel
展开最多32次。这32次展开与要求Dafny对每个可能值进行案例分析基本相同。然后Dafny可以在没有额外帮助的情况下完成证明。
lemma {:fuel pow2,31,32} pow2Bounds(n: nat)
requires n < 32
ensures 0 <= pow2(n) < 0x100000000
{}
:fuel
属性(轻微)记录在第24节的Dafny Reference Manual中。
答案 1 :(得分:1)
有点作弊,但由于域名非常狭窄,这种方法很有效。
const pow2: seq<u32> :=
[0x1, 0x2, 0x4, 0x8, 0x10, 0x20];
lemma pow2_exponential(n: u32)
ensures n == 0 ==> pow2[n] == 1
ensures 0 < n < 6 ==> pow2[n] == 2 * pow2[n - 1]
{}