本篇前置:
- IncRe[4] CTM 9 Relational Programming 前半部分
https://www.cnblogs.com/minor-second/p/15692707.html
基础信息
- 书名:Concepts, Techniques, and Models of Computer Programming
- ISBN:978-0-262-22069-9
- 12 Constraint Programming
- Incentive:本学期课程有一门“约束式编程”,是一种非常有意思的paradigm. 想弄明白其在各种programming paradigm全景中的位置。
12 Constraint Programming
CSP(约束满足问题):找值,使得满足一系列条件
- 暴力一般可解
- 约束编程的一些手段减小计算量
- 但一般不能完全去除某种意义上的“搜索”过程
Solving CSPs is related to deep questions of intractability. Problems that are known to be intractable will always need some search. The hope of constraint programming is that, for the problems that interest us, the search component can be reduced to an acceptable level.
某种意义上,最接近声明式的目的:只说做什么,不说怎么做
(其实logic也有这种想法)
12.1 Propagate-and-search
三个核心思想
- Keep partial information: “缩小了解的范围”这种“阶段性成果”应保留
- local deduction: 局部地推理,推出更多阶段性成果
- 适当选择分割(split, choice)规则。每次把\(P\)变成\(P(P\wedge C) \vee (P\wedge \neg C)\)之后,充分利用新加入的\(C\)之类的做推理
例程
declare X Y in
X::90#110
Y::48#53
declare A B in
A::0#10000
A=:X*Y
B::4500#10000
B=:X*Y
{Show A>:4000}
{Show A}
{Show B}
输出
1
A{4320#5830}
B{4500#5830}
-
::
以及#
用来定义取一定范围内整数值 -
=:
添加约束 -
A>:4000
恒成立,故为1
(真)- 如果把4000改成4500,那么输出
_{0#1}
,表示可能为真或为假
- 如果把4000改成4500,那么输出
- 可以看到
B
的定义域被截了一下
前一部分是一些约束(书中称为propagators),后一部分是定义域。这样的\(S_1\)叫computation space
通过多次局部推理(即约束传播),缩减定义域至不能再减,然后不得不开始搜索(但搜索范围小很多了)。迭代进行两个过程
把在约束传播下稳定了的\(S_1\)分解成\(S_2\)和\(S_3\)
约束求解例程
declare
proc {Rectangle Sol}
sol(X Y)=Sol
in
X::1#9
Y::1#9
X*Y=:24
X+Y=:10
{FD.distribute naive Sol}
end
{Show {Search.base.all Rectangle}}
12.2 Programming techniques
\(SEND+MORE=MONEY\)问题:关注不等号\=:
,关注一个特别的约束{FD.distinct Sol}
,关注ff
(first fail)策略
约束求解的更多实际问题:建模。效率问题(改变约束形式,改变搜索策略……等等方法提高效率)
回文数相关问题中用第9章的choice
和本章的约束求解算法,效率差别很大。原因是
- 基于约束求解的算法(特别地,有约束传播,动态生成检索树)比暴力
choice
快很多 - 挖掘问题结构(被11整除),进一步提升效率
To write a good specification, you have to understand the operational meaning of the constraints as well as the logical meaning. The latter is enough for showing correctness, but the former is essential to get good performance.
其实从更大角度来看,constraint logic programming就是一种operational semantics,且是效率很高,演绎能力很强的一种
12.3 The constraint-based computation model
A computation space collects together basic constraints and propagators
也就是encapsulation
propagator是一个可以往store添加约束的线程(从并发的思想理解约束传播!)
computation space可以嵌套,子space可以看到所有父母的约束
- Basic constraints: 可直接在store中表示
x = person(age:y)
(binding of a dataflow variable)是一个例子(equality constraint)。第2章这种bindings是约束的特例。
本章新增Basic constraints形式:::
,表示定义域包含于某某。可以有多个此类语句,取交集。 - relational tree: 名为树,实际可能有环,需要存的是图。比如
X=foo(X)
自己连自己。增加约束可以缩减relational tree的定义域 - propagator等待定义域变化就往store增加约束,是waiting for determinacy的一种具体体现
- propagator间是“declarative concurrent”,即我们需要它们运行结果“稳定”,但不关心其具体传播顺序等细节
借助computaion space,容易编写两方面策略:搜索策略和分类(distribution)策略。分类就是分几类,加什么约束去分。搜索就是深度优先,广度优先……等。怎么分类和怎么搜索可以是完全正交的(不管怎么分,我都深搜)。但是也可以有联系,比如记忆之前结果,动态改变分类和搜索策略等。
总结和问答练习
- Q: 把
Rectangle
相关的那个例程用第9章的choice
改写(不使用本章的::
和=:
等)
A:
declare
fun {Digit}
choice 1 [] 2 [] 3 [] 4 [] 5 [] 6 [] 7 [] 8 [] 9 end
end
declare
proc {Rectangle Sol}
sol(X Y)=Sol
in
X={Digit}
Y={Digit}
X*Y=24
X+Y=10
end
{Show {Search.base.all Rectangle}}
注:本章没有指定choice
(显式指定搜索方式),则需要其它地方指定怎么搜索(一般来说,搜索树是过程中动态创建和变化的。搜索策略需要写在约束求解器中)。具体地,Rectangle
相关那个例程直接调包:{FD.distribute naive Sol}
- Q: 并行和并发有什么区别?
A:
来自Erlang之父Joe Armstrong
本章更关注约束求解的并发本质。实际上,为了提升效率,也可以引入并强调并行(比如把传播各自局限在一些变量,两个cpu核分别跑一部分变量相关的传播这样)