【问题标题】:Reducing constraints in Prolog减少 Prolog 中的约束
【发布时间】:2019-12-15 21:30:07
【问题描述】:

One of the recent Advent of code challenges 要求我解决最小量的输入材料,我可以使用这些材料来应用一组给定的反应并获得 1 个单位的输出材料。

例如,给定

10 ORE => 10 A
1 ORE => 1 B
7 A, 1 B => 1 C
7 A, 1 C => 1 D
7 A, 1 D => 1 E
7 A, 1 E => 1 FUEL

我们总共需要 31 块矿石来生产 1 种燃料(1 块用来生产单位 B,然后 30 块用来生产必需的 28 A)。

今年,我一直在努力拓展我的编程语言视野,因此我已经完成了 SML/NJ 的大部分挑战。这个似乎——似乎——很适合 Prolog,因为我对它知之甚少:逻辑编程、约束求解等。

I was able to put together a working version of this, with some help,我抽空阅读了一些关于 CHR 编程的教程。我想我基本上明白,下面,我们说如果我们知道 10 个a(1) 项目(这些叫什么?证明?),那么我们可以用ore(10) 替换它——这将扩展为 10 个单独的ore(1)来电。

%prolog

:- module(ore, [ fuel/1 ]).
:- use_module(library(chr)).
:- use_module(library(clpfd)).

% constraint names
% ore and fuel are guaranteed
:- chr_constraint
    ore/1,
    a/1,
    b/1,
    c/1,
    d/1,
    e/1,
    fuel/1.

% problem constraints
a(1),a(1),a(1),a(1),a(1),a(1),a(1),a(1),a(1),a(1) <=> ore(10).
b(1) <=> ore(1).
c(1) <=> a(7), b(1).
d(1) <=> a(7), c(1).
e(1) <=> a(7), d(1).
fuel(1) <=> a(7), e(1).

% decompositions (one per element???)
a(X) <=> X #> 1, Y #= X-1 | a(Y), a(1).
b(X) <=> X #> 1, Y #= X-1 | b(Y), b(1).
c(X) <=> X #> 1, Y #= X-1 | a(Y), a(1).
d(X) <=> X #> 1, Y #= X-1 | d(Y), d(1).
e(X) <=> X #> 1, Y #= X-1 | e(Y), e(1).
ore(X) <=> X#> 1, Y #= X-1 | ore(Y), ore(1).

% aggregation (for convenience) 
% always present
:- chr_constraint ore_add/1, total_ore/1.

total_ore(A), total_ore(Total) <=> NewTotal #= A + Total, total_ore(NewTotal).

ore(1) <=> total_ore(1).

如果能够写出以下内容,那就太好了:

a(10) <=> ore(10)

并以某种方式告诉 prolog,如果我“知道”,例如 a(8),我仍然可以替换 ore(10)(因为我需要 10 个矿石来制造这 8 个 a,而有些只是浪费了)。

认为如果我能做到这一点,我就可以把这个输出

?- fuel(1).
ore:a(1),
ore:a(1),
ore:a(1),
ore:a(1),
ore:a(1),
ore:a(1),
ore:a(1),
ore:a(1),
ore:total_ore(21).

进入

?- fuel(1).
ore:total_ore(31).

如果我手动将,ore:a(2) 添加到燃料查询中,我会得到正确的总矿石。

总结一下,

  1. 如何表达我不能部分地运行反应,但我可以运行反应以获得比我需要的更多的约束?换句话说,如果我需要a(8),就足以知道我需要a(10)?我认为,这也将允许我用a(10) &lt;=&gt; ore(10) 之类的语言编写原始问题陈述,而不是荒谬的a(1),a(1)...。还是会?
  2. 这会解决我的“搜索”问题,例如fuel(1) 将给我ore:total_ore(31)

更新:我花了一些时间玩 (1) 来获得

%prolog

:- module(ore, [ fuel/1 ]).
:- use_module(library(chr)).
:- use_module(library(clpfd)).

% constraint names
% ore and fuel are guaranteed
:- chr_constraint
    ore/1,
    a/1,
    b/1,
    c/1,
    d/1,
    e/1,
    fuel/1.

% problem constraints
a(X) <=> X #>= 1, X #=< 10 | ore(10).
b(1) <=> ore(1).
c(1) <=> a(7), b(1).
d(1) <=> a(7), c(1).
e(1) <=> a(7), d(1).
fuel(1) <=> a(7), e(1).

% decompositions (one per element???)
b(X) <=> X #> 1, Y #= X-1 | b(Y), b(1).
c(X) <=> X #> 1, Y #= X-1 | a(Y), a(1).
d(X) <=> X #> 1, Y #= X-1 | d(Y), d(1).
e(X) <=> X #> 1, Y #= X-1 | e(Y), e(1).
ore(X) <=> X#> 1, Y #= X-1 | ore(Y), ore(1).

% aggregation (for convenience) 
% always present
:- chr_constraint ore_add/1, total_ore/1.

total_ore(A), total_ore(Total) <=> NewTotal #= A + Total, total_ore(NewTotal).

ore(1) <=> total_ore(1).

为我的查询生成了total_ore(41)我相信“剩菜”正在转化为矿石,在这种情况下,这不是我想要的(尽管它当然是指定的)。as 现在被删除得太早了——即,这是正确的,但不是最小的。如何喜欢整个反应?嗯……

【问题讨论】:

  • 如果真的有必要,我可以把 3 分成它自己的问答;我认为这样看起来更干净,因为它们有点纠结。不过,1/2 确实属于一起。
  • "(这些叫什么?证明?)" 它们被称为约束。但我不确定约束处理规则是否适合这个问题。但它们实际上是“向前”链接的(使用多集状态,我还没有弄清楚如何考虑这一点),而查询“我需要多少东西才能获得 X 单位的燃料”似乎完全匹配反向链接 horn-clausy Prolog 引擎,可通过适当的回溯获得最小化。
  • 这可能也是一个整数 CLP 问题,但我不知道该怎么做。
  • @DavidTonhofer 谢谢;我使用了约束,因为上一个问题就是这样开始的。我同意反向链;会欢迎对原始问题的回答

标签: prolog swi-prolog clpfd constraint-handling-rules


【解决方案1】:

我能够得到准确的答案使用

%prolog

:- module(ore, [ fuel/1 ]).
:- use_module(library(chr)).
:- use_module(library(clpfd)).

% constraint names
% ore and fuel are guaranteed
:- chr_constraint
    ore/1,
    fuel/1.

% aggregation
% always present
:- chr_constraint ore_add/1, total_ore/1.
total_ore(A), total_ore(Total) <=> NewTotal #= A + Total, total_ore(NewTotal).
ore(X) <=> total_ore(X).

% BEGIN TO GENERATE

:- chr_constraint
    a/1,
    b/1,
    c/1,
    d/1,
    e/1.

% problem constraints (script to generate these?)
a(X), a(Y) <=> S #= X+Y, S #> 10, L #= S - 10 | ore(10), a(L).
a(X), a(Y) <=> S #= X+Y, S #=< 10 | ore(10).
b(1) <=> ore(1).
c(1) <=> a(7), b(1).
d(1) <=> a(7), c(1).
e(1) <=> a(7), d(1).
fuel(1) <=> a(7), e(1).

a 上的限制说

  1. 如果我们需要X+Y,其中X+Y &gt; 10,那么我们可以用10个矿石来做,但我们还有10-(X+Y)一个剩余;和
  2. 如果我们需要X+Y a,其中X+Y &lt; 10,那么我们也可以使用 10 块矿石并且没有剩菜(不幸的是,我们从来没有看到浪费了)。

我相信排序要求 prolog 更喜欢第一条规则,这有助于我们的最小化问题。不过,我还没有在其他版本的问题上测试这种策略——我怀疑生产的无形浪费可能会咬我。

这给了

?- fuel(1).
ore:total_ore(31)

对于更复杂的问题,关键是:每当反应产生 x 中的 1 个时,使用 na,您可以将其编码为

x(X) <=> A #= X*n | a(A).

当反应产生Ax 时,您可以将其编码为

x(A) <=> ... .
x(X) <=> X #> A, L #= X - A | ..., x(L).
x(X), x(Y) <=> S #= X+Y, S #> A, L #= S - A | ..., x(L).
x(X), x(Y) <=> S #= X+Y, S #=< A | ... .

对于某些问题,但不是全部,您还需要以下内容 - 有时您需要它来处理剩菜,但其他时候,由于最小化,您希望避免此规则...尤其是当还有其他反应需要执行时。

x(X) <=> X #=< A | ... .

最后一点的一个很好的测试用例是

9 ORE => 2 A
8 ORE => 3 B
7 ORE => 5 C
3 A, 4 B => 1 AB
5 B, 7 C => 1 BC
4 C, 1 A => 1 CA
2 AB, 3 BC, 4 CA => 1 FUEL

可以用

解决
%prolog

:- module(ore, [
    fuel/1,
    total_ore/1
    ]).
:- use_module(library(chr)).
:- use_module(library(clpfd)).

% constraint names
% ore and fuel are guaranteed
:- chr_constraint
    ore/1,
    fuel/1.

% aggregation
% always present
:- chr_constraint total_ore/1.
total_ore(A), total_ore(Total) <=> NewTotal #= A + Total, total_ore(NewTotal).
ore(X) <=> total_ore(X).

% BEGIN TO GENERATE

:- chr_constraint
    a/1,
    b/1,
    c/1,
    ab/1,
    bc/1,
    ca/1.

% 9 ORE => 2 A
% 8 ORE => 3 B
% 7 ORE => 5 C
% 3 A, 4 B => 1 AB
% 5 B, 7 C => 1 BC
% 4 C, 1 A => 1 CA
% 2 AB, 3 BC, 4 CA => 1 FUEL

% problem constraints (script to generate these?)
a(2) <=> ore(9).
a(X) <=> X #> 2, L #= X - 2 | ore(9), a(L).
a(X), a(Y) <=> S #= X+Y, S #> 2, L #= S - 2 | ore(9), a(L).
a(X), a(Y) <=> S #= X+Y, S #=< 2 | ore(9).
a(X) <=> X #< 2 | ore(9).
b(3) <=> ore(8).
b(X) <=> X #> 3, L #= X - 3 | ore(8), b(L).
b(X), b(Y) <=> S #= X+Y, S #> 3, L #= S - 3 | ore(8), b(L).
b(X), b(Y) <=> S #= X+Y, S #=< 3 | ore(8).
b(X) <=> X #<3 | ore(8).
c(5) <=> ore(7).
c(X) <=> X #> 5, L #= X - 5 | ore(7), c(L).
c(X), c(Y) <=> S #= X+Y, S #> 5, L #= S - 5 | ore(7), c(L).
c(X), c(Y) <=> S #= X+Y, S #=< 5 | ore(7).
a(X) <=> X #< 5 | ore(7).
ab(X) <=> A #= 3*X, B #= 4*X | a(A), b(B).
bc(X) <=> B #= 5*X, C #= 7*X | b(B), c(C).
ca(X) <=> A #= X, C #= 4*X | a(A), c(C).
fuel(1) <=> ab(2), bc(3), ca(4).

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多