SWI-Prolog CLPFD
(2)
Tienes razón de que 1//5
sería una poda óptima en este caso.
Sin embargo, por razones de eficiencia, los sistemas CLP (FD) típicamente mantienen solo la llamada consistencia de límites para las restricciones aritméticas, y en general no eliminan los elementos interiores de los dominios incluso cuando algunos de ellos no pueden participar en las soluciones.
La consistencia de los límites , en el caso finito, significa que hay soluciones donde la variable asume los límites inferior y superior del dominio. En este caso, hay soluciones para A=1
y A=5
.
Tenga en cuenta que estas son las únicas soluciones en este caso concreto, pero en general, también hay soluciones con puntos interiores en instancias análogas más grandes, por ejemplo:
?- [A,B] ins 1..10, A*B#=10, label([A,B]). A = 1, B = 10 ; A = 2, B = 5 ; A = 5, B = 2 ; A = 10, B = 1.
Sin embargo, la buena noticia es que el número de dichas soluciones solo crece de forma logarítmica en el tamaño del dominio:
?- length(_, Exp), N #= 2^Exp, [A,B] ins 1..N,A*B#=N,
findall(., label([A,B]), Ls), length(Ls, L),
writeln(Exp-L), false.
0-1
1-2
2-3
3-4
4-5
5-6
6-7
7-8
etc.
Esto contrasta con otros casos, como X mod 2 #= 0
, donde el número de soluciones crece linealmente en el tamaño del dominio de X
(y, por tanto, exponencialmente en la longitud de su representación decimal), y por lo tanto no es factible para podar el dominio explícitamente
Por lo tanto, como una solución factible, puede usar label/1
para obtener soluciones concretas, y luego usar in/2
restricciones para restringir los operandos a sus dominios concretamente admisibles:
:- use_module(library(clpfd)).
stricter_domains(Vs) :-
findall(Vs, label(Vs), Sols0),
transpose(Sols0, Sols),
maplist(list_to_domain, Sols, Ds),
maplist(in, Vs, Ds).
list_to_domain([L|Ls], Dom) :- foldl(dom_disj, Ls, L, Dom).
dom_disj(D0, I, D0//I).
Tu ejemplo:
?- [A,B] ins 1..5, A*B#=5, stricter_domains([A,B]). A in 1//5, A*B#=5, B in 1//5.
Soy nuevo en Prolog para la programación de restricciones. Tengo un problema con CLPFD que no reduce un dominio como espero. Esto es probablemente muy simple.
[A,B] ins 1..5,A*B#=5.
Espero que reduzca el dominio de A y B a
1//5
Pero solo da
A in 1..5,
A*B#=5,
B in 1..5.
Cualquier sugerencia sera apreciada.
Si bien esta respuesta se adapta a clpfd tal como se implementa en swi-prolog , la idea / método es portátil.
:- use_module(library(clpfd)).
Así es como podemos reducir los tamaños de dominio antes de comenzar la enumeración completa :
shave_zs(Zs) :-
maplist(flag_zs_shave_z(F,Zs), Zs),
once((var(F) ; ground(Zs) ; shave_zs(Zs))).
flag_zs_shave_z(Flag, Zs, Z) :-
( fd_size(Z, sup)
-> true % never shave the infinite
; fd_dom(Z, Z_dom),
phrase(dom_integers_(Z_dom), Z_vals),
maplist(flag_zs_z_val(Flag,Zs,Z), Z_vals)
).
flag_zs_z_val(Flag, Zs, Z, Z_val) :-
( /+ call_with_inference_limit((Z #= Z_val,labeling([],Zs)), 1000, _)
-> Z #/= Z_val,
Flag = true
; true
).
Utilizamos la gramática dom_integers_//1
, tal como se define en la página de manual de clpfd de SWI-Prolog:
dom_integers_(I) --> { integer(I) }, [I].
dom_integers_(L..U) --> { numlist(L, U, Is) }, Is.
dom_integers_(D1//D2) --> dom_integers_(D1), dom_integers_(D2).
Consultas de muestra:
?- [A,B] ins 1..5, A*B #= 5, (Shaved = false ; Shaved = true, shave_zs([A,B])).
Shaved = false, A*B #= 5, A in 1..5, B in 1..5 ;
Shaved = true, A*B #= 5, A in 1//5, B in 1//5.
?- [A,B] ins 1..10, A*B #= 10, (Shaved = false ; Shaved = true, shave_zs([A,B])).
Shaved = false, A*B #= 10, A in 1..10 , B in 1..10 ;
Shaved = true, A*B #= 10, A in 1..2//5//10, B in 1..2//5//10.