Murphi入门(五)—— 用murphi实现GCD(2)
该博文是对之前GCD编程的改进:
- 未改进的GCD地址
- 改进思路
- 通过GCD,对Murphi编程的思想做个总结
Mar 7 GCD 改进
目标
- 改进GCD
- 通过GCD求解,更深刻理解Murphi与其他编程原理的不同
思想
Murphi最主要的思想是,按照所有全局变量的赋值来考虑,亦即考虑状态。因为是通过遍历所有状态来达到检验的目的。思考路线如下:
x | y | arr_i | arr_temp | |
---|---|---|---|---|
inital | 12 | 21 | [1,3] | [1,3] |
swap | 21 | 12 | [1,3] | [1,3] |
gcd | 21 | 12 | [1,3] | [1,3] |
这是根据rule的迁移而写的状态。可以看到,全局变量有x, y, arr_i, arr_temp。其中,x,y为需要求gcd的两个数。两个数组是用来保证invariant性质的。
规则
有两种思路:
函数思路:
用常规编程的思想来思考这个程序,比如python语言中,用递归:
1 | def gcd(x,y): |
或者不用递归,用交换和循环:
1 | def gcd(x,y): |
可以看到,有两个步骤:
- 交换
- 循环直到找到最大公因数
所以将这个思路用于murphi规则:
- rule “swap”
- 条件:x < y
- 动作:交换x,y的值
- rule “gcd”
- 条件:x > y
- 动作:循环,求出gcd
代码实现:
1 | rule "swap" -- 交换 |
状态思路
因为全局变量有x和y,x与y的关系有3种可能:
- x > y
- x = y
- x < y
其中,第二种直接可以输出x。第一种可以执行gcd(x,y),第二种执行gcd(y,x)。将这个思路直接写成规则即可。
- rule “gcd_x”
- 条件:x > y
- 动作:gcd(x,y)
- rule “equal”
- 条件:x = y
- 动作:put(x)
- rule “gcd_y”
- 条件:x < y
- 动作:gcd(y,x)
代码如下:
1 | rule "gcd_x" |
初始状态
x | y | arr_i | arr_temp | |
---|---|---|---|---|
initial | 21 | 12 | [1,3] | [1,3] |
代码实现:
1 | startstate |
不变量
在过程中,x,y的值都设置为不变,则此时不变的为x,y的公因数集合。每次状态的迁移都必须满足该表达式。即保证x,y不会改变。
代码:
1 | invariant "commonDivdors" |
函数 / 程序
函数与程序的区别为,函数有返回值,程序没有返回值。
在传递参数时,传入的参数都为引用传递,var开头表示在函数体内部可改变其值,没有var开头则为不可改变值。
如果要传入数值,则只能用不带var开头的形参。
要实现的函数/程序有:
- gcd — 求解最大公因数
- commonDivdors —求解x,y的公因数集合
- compare —用来比较两个数组是否相同
具体实现如下:
gcd
1 | Procedure gcd(x,y: val_t); |
commonDivdors
1 | Procedure commonDivdors(x, y: val_t; Var arr_temp:arr_t); |
compare
1 | Function compare(arr_temp, arr_i: arr_t): Boolean; -- 有返回值 |
运行
*死锁:某状态只有该状态本身之外,没有其他后继状态。
因为程序设置x,y的值不变(或互换之后,公因数集仍然没有改变),所以对于规则而言,一开始满足的,后来也会满足。所有造成死锁。但没关系,因为状态枚举完成,且会输出最大公因数。(用例: x = 24, y = 18, 规则用第二种方案)
1 | ./gcd_2.o -pr |
结果如下:
1 | b:6 |
总结
- murphi编程与一般编程的思维方式不太一样,要考虑全局状态的迁移。
- 可以写出状态的表格,更有利于分析和理解。
- 当分析了所有状态之后,规则集也就方便写出了。
- 不变量的选择很重要。