使用prolog和clpr约束系统 - java

我希望在桌面应用程序中使用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)的语法:

向量([X1,X2,X3,X4,X5,X6,X7]):-
{X1> X2 + X3 + X4,
X5 = <sin(X6 + X7)}。

特别注意使用=<以及正确使用{}/1来表示CLP(R)约束。在Prolog算术中避开了标记<=,因为它看起来像一个箭头,通常表示在证明中的含义。

即使尚未将其实例化为具体的解决方案,这也足以获得第一个答案:

α-向量。
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]),
随机(A)
随机(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]),
随机(A)
随机(B)
随机(C)
随机(D)。
假。

在此,(确定性!)随机数生成与约束冲突。有几种方法可以解决此问题。在显示它们之前,让我们使用以下定义将变量约束到所需的间隔:

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]),
随机(A)
随机(B)
随机(C)。

这再次产生false

方法1:更多相同

解决上述问题的一种方法是简单地重复随机分配,直到找到回溯解决方案为止:

α-vector([A,B,C,D,E,F,G]),
maplist(zero_to_one,[A,B,C,D,E,F,G]),
随机(A)
随机(B)
重复,
随机(C)。
A = 0.9433451780634803,
B = 0.15859272177823736,
C = 0.706502025956454,
{D> = 0.0,_2064 = 0.07825043032878898-D,D <0.07825043032878898},
{E> = 0.0,E = = 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®中的maximize/1和/或minimize/1来使用约束求解器本身来获取具体的解决方案。这仅适用于线性约束,甚至不适用于所有约束。例如,考虑以下查询:

?-{X = sin(Y)},
maplist(zero_to_one,[X,Y]),
最大化(X)。
假。

乃至:

?-{X <1},maximum(X)。
假。

虽然相反:

α-{X = <1},最大化(X)。
X = 1.0。

现在,让我们使用以下技巧来消除所有非线性约束:我们仅使用例如,将随机浮点数分配给X6X7即可:

?-向量(Ls),
Ls = [A,B,C,D,E,F,G],
maplist(zero_to_one,Ls),
随机(F),随机(G)。

在此基础上,我们可以编写:

?-向量(Ls),
Ls = [A,B,C,D,E,F,G],
maplist(zero_to_one,Ls),
随机(F),随机(G),
最大化(A),最小化(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非常适合这种推理,您可以使用它轻松尝试不同的策略。
您可能必须在随机化和确定性优化之间找到一个可行的折衷方案,以实例化剩余的矢量分量。权衡也可能取决于载体成分的包容性。

最后,一个非常重要的说明:隐式随机状态与逻辑关系中我们期望的属性背道而驰,因为它们会导致您的谓词在后续调用中表现得截然不同,从而使调试和系统测试成为噩梦。因此,我强烈建议您为随机种子做好准备,或者通过代码携带随机数生成器的显式状态。这将帮助您更好地了解程序的行为并使其完全具有确定性。您以后可以更改种子以生成不同的解决方案集合。

Java中的“ <<”运算符 - java

最喜欢的语句来自Java的Character类:(1 << Character.PARAGRAPH_SEPARATOR)) >> type PARAGRAPH_SEPARATOR是字节,type是整数。这句话中的操作员,他们做什么?如何以及在哪里可以使用这些运算符?这是oracles java.lang.Character文档。该类中…

JAVA:字节码和二进制有什么区别? - java

java字节代码(已编译的语言,也称为目标代码)与机器代码(当前计算机的本机代码)之间有什么区别?我读过一些书,他们将字节码称为二进制指令,但我不知道为什么。 参考方案 字节码是独立于平台的,在Windows中运行的编译器编译的字节码仍将在linux / unix / mac中运行。机器代码是特定于平台的,如果在Windows x86中编译,则它将仅在Win…

java:继承 - java

有哪些替代继承的方法? java大神给出的解决方案 有效的Java:偏重于继承而不是继承。 (这实际上也来自“四人帮”)。他提出的理由是,如果扩展类未明确设计为继承,则继承会引起很多不正常的副作用。例如,对super.someMethod()的任何调用都可以引导您通过未知代码的意外路径。取而代之的是,持有对本来应该扩展的类的引用,然后委托给它。这是与Eric…

Java:BigInteger,如何通过OutputStream编写它 - java

我想将BigInteger写入文件。做这个的最好方式是什么。当然,我想从输入流中读取(使用程序,而不是人工)。我必须使用ObjectOutputStream还是有更好的方法?目的是使用尽可能少的字节。谢谢马丁 参考方案 Java序列化(ObjectOutputStream / ObjectInputStream)是将对象序列化为八位字节序列的一种通用方法。但…

Java DefaultSslContextFactory密钥库动态更新 - java

我有一个使用org.restlet.engine.ssl.DefaultSslContextFactory的现有应用程序和一个在服务器启动时加载的密钥库文件。我有另一个应用程序,该应用程序创建必须添加的证书服务器运行时动态地更新到密钥库文件。为此,我在代码中创建了证书和私钥,然后将其写入到目录。该目录由bash脚本监视,该脚本检查是否有新文件,如果出现,它将…