【问题标题】:Prolog Break Money into Smaller AmountsProlog 将资金分成较小的金额
【发布时间】:2015-03-30 04:37:21
【问题描述】:

我有这个谓词,如果 S 等于某个等式,例如 K + 2N + 3L = S,则返回 true。 对于 K、N,我们拥有的钱分别是 1、5 和 10, L.

我不想使用 :- use_module(library(clpfd)),我想在没有它的情况下解决这个问题。

我的直觉是将其分解为子问题,例如编写函数breakMoney1(S,K) :- K is S。并通过添加一个参数创建更多助手但是当我比较时,我正在努力解决获取未实例化变量的问题。

 breakMoney(S,K,N,L) :- 

【问题讨论】:

  • 在比较之前使用between/3实例化它们。
  • Prolog 没有函数。它有谓词。他们不是一回事。 function 是命令式的(“做某事”)。 谓词表示关系规则。不幸的是,您希望避免使用 CLPFD,因为它是纯 Prolog 表达数值表达式之间的 关系 的方式。对于你的问题,你说你的代码有这样那样的问题,但你没有显示你的代码。也许您可以展示您的尝试。

标签: prolog


【解决方案1】:

这可能比您想象的要容易。遵循@Will Ness 的建议的一个非常天真的解决方案是:

break(Sum, K, N, L) :- integer(Sum), Sum >= 0,

    % upper bounds for K, N, and L
    K_Max is Sum div 1,
    N_Max is Sum div 5,
    L_Max is Sum div 10,

    % enumerate possible values for K, N, and L
    between(0, L_Max, L),
    between(0, N_Max, N),
    between(0, K_Max, K),

    Sum =:= K + 5*N + 10*L.

这将“神奇地”轻松地变成 clp(fd) 解决方案:例如,将 between 替换为 X in 0..Max,将 =:= 替换为 #=。虽然,对于每个面额来说,简单地说X #>= 0 就足够了。这是一个很好的练习,看看您可以删除多少约束并仍然得到答案:

break(Sum, K, N, L) :-
    K #>= 0, N #>= 0, L #>= 0,
    Sum #= K + 5*N + 10*L.

根据您实例化参数的方式,您可能会立即得到唯一的答案,或者您可能需要使用label/1

?- break(100, P, 8, 5).
P = 10.

?- break(10, K, N, L).
K in 0..10,
-1*K+ -5*N+ -10*L#= -10,
N in 0..2,
L in 0..1.

?- break(10, K, N, L), label([K, N, L]).
K = N, N = 0,
L = 1 ;
K = L, L = 0,
N = 2 ;
K = 5,
N = 1,
L = 0 ;
K = 10,
N = L, L = 0.

但正如@lurker 所指出的,几乎没有理由不使用约束编程来解决这个问题。当然,除非你有一个非常聪明的算法来解决这个特定的问题,并且你知道它会比通用的 clp(fd) 解决方案更聪明。即使这样,使用labelling/2 的选项也可能达到相同的效果。

【讨论】:

  • 一个不那么天真的枚举可能是between(0,L_Max,L), S1 is Sum - L*10, Nmax is S1 div 5, between(0,Nmax,N), S2 is S1 - N*5, 0 is S2 rem 1, K is S2 div 1, ...。另一种方法完全是允许总和也是未知的,并以某种方式按照它们的总和的顺序生成所有可能性,可能以类似于如何生成汉明(“丑陋”)数字的方式。可能必须为此模拟流,例如hereherehere
猜你喜欢
  • 2022-06-13
  • 2013-10-14
  • 2019-04-16
  • 2011-04-13
  • 1970-01-01
  • 1970-01-01
  • 2016-10-21
  • 2016-03-17
  • 1970-01-01
相关资源
最近更新 更多