我希望在桌面应用程序中使用prolog来生成满足约束系统的随机向量.
例如,我们的用户可以在运行时为我们的软件提供以下信息:
给定向量< x1,x2,x3,... x30> ;,我们可能有两个约束:
x1 > x2 + x3 + x4
x5 <= sin(x6 + x7)
我想做的是生成一个大致遵循以下形式的序言程序:
:- random(0.0, 1.0, X1)
:- random(0.0, 1.0, X2)
#...
# its also concievable that the bounds are different than 0 to 1
:- random(0.0, 1.0, X30)
clp(r) :- constraints {
X1 > X2 + X3 + X4,
X5 <= sin(X6 + X7)
}
?- [ X1, X2, X3, X4, ... X30 ]
它将在30维空间中输出均匀随机的向量.
序言是否可行?
还有消耗该输出的问题.我想做的是调用next()重新生成一个新的向量.具体来说,我需要避免重新编译,因为我希望能够每秒生成大约10,000个这些向量.我可以达到这种性能水平吗?
我希望在运行我们其余软件的JVM上使用嵌入式(进程内)swi-prolog实例.这样就足够了吗?
解决方法:
没关系!
从原则上讲,这种方法是可行的,就我个人而言,我认为Prolog是执行此类任务的不错选择.
但是,您需要解决一些细微问题.
首先,让我们正确掌握CLP(R)的语法:
vector([X1,X2,X3,X4,X5,X6,X7]) :- { X1 > X2 + X3 + X4, X5 =< sin(X6 + X7) }.
特别注意== <以及正确使用{} / 1来表示CLP(R)约束.在Prolog算术中避开了标记< =,因为它看起来像一个箭头,通常表示在证明中的含义. 即使尚未将其实例化为具体的解决方案,这也足以获得第一个答案:
?- vector(Ls). Ls = [_1028, _1034, _1040, _1046, _1052, _1058, _1064], {_1046=_1028-_1034-_1040-_1088, _1088>0.0}, {_1052-sin(_1058+_1064)=<0.0}, {_1052-sin(_1058+_1064)=<0.0}, {_1052-sin(_1058+_1064)=<0.0}.
使用random / 1,我们可以将(0,1)中的随机浮点数分配给任何变量.例如:
?- vector([A,B,C,D,E,F,G]), random(A), random(B). A = 0.33797712696696053, B = 0.7039688010209147, {D= -0.3659916740539542-C-_894, _894>0.0}, {E-sin(F+G)=<0.0}, {E-sin(F+G)=<0.0}, {E-sin(F+G)=<0.0}.
这解决了任务的一部分.但是,这种方法在以下情况下使我们失败:
?- vector([A,B,C,D,E,F,G]), random(A), random(B), random(C), random(D). false.
在此,(确定性!)随机数生成与约束冲突.有几种方法可以解决此问题.在显示它们之前,让我们使用以下定义将变量约束到所需的时间间隔:
zero_to_one(X) :- { 0 =< X, X =< 1 }.
我们可以简单地将此约束声明为一项附加要求:
?- vector([A,B,C,D,E,F,G]), maplist(zero_to_one, [A,B,C,D,E,F,G]), random(A), random(B), random(C).
这再次产生错误.
方法1:更多相同
解决上述问题的一种方法是简单地重复随机分配,直到找到回溯解决方案为止:
?- vector([A,B,C,D,E,F,G]), maplist(zero_to_one, [A,B,C,D,E,F,G]), random(A), random(B), repeat, random(C). A = 0.9433451780634803, B = 0.15859272177823736, C = 0.706502025956454, {D>=0.0, _2064=0.07825043032878898-D, D<0.07825043032878898}, {E>=0.0, E=<1.0, F>=0.0, F==0.0, G=<1.0, E-sin(...)=<0.0}, {E-sin(F+G)=<0.0}, {E-sin(F+G)=<0.0}, {E-sin(F+G)=<0.0} .
因此,我们离具体的解决方案又近了一步,这意味着向量的完整实例化.缺点非常明显:在极端情况下,我们将永远不会以这种方式找到有效的分配.如果运气稍好,甚至可能需要为尝试单个附加变量找到具体值而进行许多尝试.
方法2:最大化或最小化
解决此问题的另一种方法是使用CLP(R)的maximum / 1和/或minimum / 1以使用约束求解器本身来获得具体的解决方案.这仅适用于线性约束,甚至不适用于所有约束.例如,考虑以下查询:
?- { X = sin(Y) }, maplist(zero_to_one, [X,Y]), maximize(X). false.
乃至:
?- { X < 1 }, maximize(X). false.
虽然相反:
?- { X =< 1 }, maximize(X). X = 1.0 .
现在,让我们使用以下技巧摆脱所有非线性约束:我们仅使用以下示例简单地将随机浮点数分配给X6和X7:
?- vector(Ls), Ls = [A,B,C,D,E,F,G], maplist(zero_to_one, Ls), random(F), random(G).
在此基础上,我们可以编写:
?- vector(Ls), Ls = [A,B,C,D,E,F,G], maplist(zero_to_one, Ls), random(F), random(G), maximize(A), minimize(B+C+D+E). Ls = [1.0, 0.0, 0.0, 0.0, 0.0, 0.9702069686491169, 0.13220925936558517], A = 1.0, B = C, C = D, D = E, E = 0.0, F = 0.9702069686491169, G = 0.13220925936558517 .
因此,我们获得了一个满足所有约束并具有一些随机成分的具体解决方案.
闭幕致辞
首先,重复一遍,我认为Prolog是执行此类任务的不错选择.约束求解器的修剪可以帮助消除大部分搜索空间,并且约束求解器本身可以帮助您通过最小化和最大化来获得具体的解决方案.其次,您仍然需要记住一些问题:
>首先,从每个解决方案同等可能性的意义上来说,以这种方式(通过任一方法)生成的解决方案不是随机的.相反,可能存在比其他解决方案更可能出现的解决方案集群.
>如上所示,这些方程可能需要一些其他的推理和实验,既可以将它们简化为线性方程,又可以应用适用的优化方向. Prolog非常适合这种推理,您可以使用它轻松尝试不同的策略.
>您可能必须在随机化和确定性优化之间找到一个可行的折衷方案,以实例化剩余的矢量分量.权衡也可能取决于载体成分的包容性.
最后,一个非常重要的说明:隐式随机状态与逻辑关系中我们期望的属性背道而驰,因为它们可能导致谓词在后续调用中表现得截然不同,从而使调试和系统测试成为噩梦.因此,我强烈建议您为随机种子做好准备,或者通过代码附带随机数生成器的显式状态.这将帮助您更好地了解程序的行为并使其完全具有确定性.您以后可以更改种子以生成不同的解决方案集合.