Как будет обрабатываться целочисленная приостановка, когда она используется в заголовке условия - PullRequest
1 голос
/ 13 мая 2019

У меня есть следующие условия для двух переменных A и B:

[A,B] #:: 1..10,
(A #= 3) or (B #= 3),
((A #> 3 or B #>3) ->
     % expression 1
;
     % expression 2
)
%cntd

Проблема в строке 2, решатель не знает о значениях A и B, как определить, какая ветвь условия будет продолжена без указания значения переменных в строке 2?

Разумным действием является выбор этой ветви на основе значения переменных, когда решатель пересекает возможные значения для переменных. Но, как я обнаружил, он проходит через одно из этих выражений, прежде чем узнает значение переменных. Как можно это предотвратить?

Ответы [ 2 ]

1 голос
/ 15 мая 2019

Constraint Programming хорошо сочетается с Prolog, если вы придерживаетесь чистой логики.Но, как вы демонстрируете, нельзя свободно смешивать процедурные элементы, такие как cut (!) и if-then-else (->;) с логикой ограничения.

Использование if-then-else или сокращений безопасно только в том случае, если условие влечет за собой (то есть, безусловно, верно) или отключено (безоговорочно, ложно) в «время установки ограничения».На практике это означает, что такие условия не должны содержать проблемных переменных (переменных домена и т. Д.), А только проблемных параметров (констант), которые известны априори.Выделенные языки моделирования различают эти две вещи, но Пролог этого не делает.

Как НЕ выражать альтернативы в моделях ограничений

Вышеприведенное означает, что вы не можете использовать cut / if- затем - чтобы выразить альтернативу, которую вы хотели выразить.Может быть заманчиво просто избавиться от аспекта условного выбора условного выбора и переписать его как чистую дизъюнкцию.Например, вы можете переписать

( Usage #>= 1000 -> Rate#=7, Bonus#=100              % WRONG
;                   Rate#=9, Bonus#=0
)

как чистую дизъюнкцию

( Usage #>= 1000, Rate#=7, Bonus#=100                % INEFFICIENT
; Usage #<  1000, Rate#=9, Bonus#=0
)

Хотя теперь это логически правильно, не делайте этого!Пролог исследует альтернативы (выраженные с помощью точки с запятой (;) или нескольких предложений) с помощью обратного отслеживания, т. Е. С нетерпением сначала выбирая одну альтернативу, а затем возвращаясь к другой.Это обычно разрушает любую надежду на эффективную программу ограничения!В программе ограничений весь поиск должен находиться в подпрограмме поиска / маркировки.

Ограничения Reified

Если и условие, и ветви альтернатив являются ограничениями, которыеЕсли у вас есть реализованная реализация (т.е. реализация, которая может отражать истинность ограничения в булевой переменной), вам повезло: вы можете переписать всю условную альтернативу с помощью специальных связок для ограничений reified (в ECLiPSe: and, or, neg, =>, #=).Для приведенного выше примера:

Usage #>= 1000  =>  Rate#=7 and Bonus#=100,            % OK
Usage #<  1000  =>  Rate#=9 and Bonus#=0

или

Usage #>= 1000 and Rate#=7 and Bonus#=100 or           % OK
Usage #<  1000 and Rate#=9 and Bonus#=0

К сожалению, только базовые арифметические ограничения имеют уточненные версии и могут комбинироваться таким образом!

Использование других встроенных ограничений

В некотором смысле, работа с альтернативами является наиболее сложной частью решения проблемы, и многие встроенные ограничения решают эту проблему.Поэтому стоит проверить, можно ли смоделировать проблему поверх существующих встроенных ограничений , при этом не имеет явных различий в модели.Кандидатами являются элемент / 3 , таблица / 2 . дизъюнктивный / 2 и многие другие.

Задержка выбора

Последнее решение - отложить исследование альтернатив до истиныусловие может быть решено однозначно.В ECLiPSe это проще всего с пунктами задержки.Используя пример OP:

delay choice(A, B) if var(A);var(B).     % wait for A,B to be known
choice(A, B) :-
    ( (A>3 ; B>3) ->                     % can now use normal Prolog tests
        write("expression 1")
    ;
        write("expression 2")
    ).

Это работает, но будет действовать только после создания экземпляров A и B.Если, как и в этом случае, условие может быть исправлено, мы можем сделать несколько лучше:

choice(A, B) :-
    Bool #= (A#>3 or B#>3),
    delayed_choice(Bool).

delay delayed_choice(Bool) if var(Bool).
delayed_choice(1) :- write("expression 1").
delayed_choice(0) :- write("expression 2").

Это будет действовать, когда условие удовлетворяется доменами:

?- choice(A, B), B #> 3.
expression 1

Превращение общих дизъюнкций в ограничение

ECLiPSe имеет изящную функцию, называемую Обобщенное распространение в библиотеке (пропия) .Это может эффективно превратить дизъюнкции Пролога в ограничение, используя простую аннотацию.Начиная с правильной, но неэффективной формулировки выше, мы можем написать:

?- ( Usage #>= 1000, Rate#=7, Bonus#=100
   ; Usage #<  1000, Rate#=9, Bonus#=0
   ) infers most.

Usage = Usage{-1.0Inf .. 1.0Inf}
Rate = Rate{[7, 9]}
Bonus = Bonus{[0, 100]}
There is 1 delayed goal.
Yes (0.00s cpu)

Как показывают домены Rate и Bonus, полезная информация была извлечена из дизъюнкции, даже до применения альтернативной альтернативы.можно решить.

0 голосов
/ 15 мая 2019

В чем проблема? Важным примечанием является использование -> (стрелка) для if-else.Когда у нас есть выражение S -> T ; U, будет вычисляться S, и если оно содержит некоторые переменные, это может иметь некоторые побочные эффекты в коде.Чтобы быть более понятным, попробуйте выполнить несколько примеров:

?-[A,B] #:: 1..10,
(A #= 3) or (B #= 3),
((A #> 3 or B #>3) ->
    write("expression 1")
;
    write("expression 2")
).

Поскольку значение A и B не определено, условие всегда выполняется, и значение expression 1 будетраспечатаны.Кроме того, результат:

A = A{1 .. 10}
B = B{1 .. 10}
There are 6 delayed goals.
Yes (0.00s cpu)

Как видите, границы A и B не меняются, так как они приостановлены для будущих выражений, и, как у нас нет, граница делаетне измените.

Теперь попробуйте другой пример:

?- [A,B] #:: 1..10,
(A #= 3) or (B #= 3),
((A #> 3 or B #>3) ->
    write("expression 1")
;
    write("expression 2")
),
A = 3. % this line is added

Поскольку значение A определено, A #> 3 неверно, но как насчет B?Это равно 3 или больше, чем 3?Как мы уже говорили, условная часть будет выполнена!Следовательно, последнее ограничение на B равно B #> 3.И, кроме выполнения write("expression 1"), результат:

A = 3
B = B{4 .. 10}
Yes (0.00s cpu)

Последний пример:

?- [A,B] #:: 1..10,
(A #= 3) or (B #= 3),
((A #> 3 or B #>3) ->
    write("expression 1")
;
    write("expression 2")
),
A = 3,
B = 3. % this line is added

Также в этом примере печатается expression 1 и результат:

No (0.00s cpu)

Это из-за того, что головка стрелки и всегда expression 1 будут выполнены.

Одно решение использует ;, оператор любит следующее:

[A,B] #:: 1..10, 
(
    (A = 3, B = 3, write('expression 21'))
    ; 
    (A = 3, B #> 3, write('expression 11'))
    ; 
    (A #> 3, B #> 3, write('expression 12'))
    ; 
    (A #> 3, B = 3, write('expression 13'))
), 
A = 3,
B = 5.

Результат приведенного выше кода:

A = 3
B = 5
Yes (0.00s cpu, solution 1, maybe more)

Ион печатает:

expression 21 expression 11

Это означает, что первая ветвь падает, но она не удалась и автоматически вернулась, и переходит к следующему случаю!и, как следующий случай применяется, все идет успешно там

...