es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Por qué esta restricción de solución de CLP(FD) se está ejecutando lentamente y cómo puedo depurarlo?

Estoy aprendiendo Prolog al hacer los desafíos de Advent of Code.

Advertencia de spoilers para el día 7 de Advent of Code 2021:

El objetivo es: dada una lista de números naturales “n_1,…, n_k”, encontrar:

min_(x\in \N) \sum_i=0^k |x – n_i|

En el código a continuación, “norm1” calcula el sumando, “cost” calcula la suma para un determinado “x” y “lowest_cost” calcula el mínimo de todos los “x”.

norm1(X, Y, N) :- N #= abs(X - Y).

cost(Nums, X, Cost) :-
    max_list(Nums, MaxX),
    min_list(Nums, MinX),
    X in MinX..MaxX,
    maplist(norm1(X), Nums, Costs),
    sum(Costs, #=, Cost).

lowest_cost(Nums, Cost) :-
    cost(Nums, X, Cost),
    once(labeling([min(Cost)], [X, Cost])).

Algunas consultas de ejemplo:

?- lowest_cost([10,11,12], Cost).
Cost = 2.

?- cost([2,4,6,8], 4, Cost).
Cost = 8.

?- cost([2,4,6,8], 5, Cost).
Cost = 8.

?- cost([2,4,6,8], 8, Cost).
Cost = 12.

?- lowest_cost([2,4,6,8], Cost).
Cost = 8.

Con listas cortas, esto resuelve correctamente y rápidamente, pero cuando lo intento con la entrada de longitud completa de 1,000, se ejecuta indefinidamente.

La forma en que imagino que esto se resolvería es simplemente probando todos los “X” en el dominio, calculando el costo para cada uno y tomando el menor. Sin embargo, esto no parece ser el caso; el tiempo parece escalar de manera superlineal con la longitud de la lista y no en absoluto con el rango de los números en la lista.

  1. ¿Cómo puedo indicarle a CLP(FD) que busque de la manera que describí, o de otra manera eficiente?
  2. ¿Cómo puedo entender mejor lo que el solucionador está tratando de hacer? “trace” es bastante desordenado y no estoy seguro de cómo obtener alguna idea de él.
Tags:  , ,

Answer

  1. Avatar for davy.ai
    1. Una forma de dirigir a CLP(FD) para que busque de manera más eficiente es utilizando la estrategia indomain_middle en lugar de la estrategia predeterminada indomain. Esta estrategia comienza dividiendo el dominio por la mitad y explorando primero el valor medio, lo cual suele ser un buen punto de partida para encontrar la solución óptima. Para utilizar esta estrategia, puedes agregar la siguiente línea antes de llamar a labeling en lowest_cost:
    labeling([min(Costo), strategy, indomain_middle], [X, Costo]).
    
    1. Para entender mejor lo que hace el solucionador, puedes utilizar el predicado fd_statistics para obtener información sobre el proceso de búsqueda. Por ejemplo, puedes agregar la siguiente línea antes de llamar a labeling en lowest_cost:
    statistics(runtime, [Inicio|_]), fd_statistics, labeling([min(Costo)], [X, Costo]), fd_statistics, statistics(runtime, [Fin|_]), Tiempo is Fin - Inicio, writeln(Tiempo).
    

    Esto imprimirá el tiempo que tarda el solucionador, así como estadísticas como el número de nodos explorados y el número de fallos. Al comparar las estadísticas de diferentes estrategias, puedes tener una idea de cuáles son más eficientes para tu problema. Además, puedes utilizar el argumento LabeledVars de fd_statistics para obtener una lista de variables y sus dominios después de la etiquetación, lo cual puede ayudarte a solucionar cualquier problema con la solución.

Comments are closed.