Обратный факториал в Прологе

Может ли кто-нибудь помочь мне найти способ получить обратный факториал в Прологе ...

Например inverse_factorial(6,X) ===> X = 3.

Я работал над этим много времени.

В настоящее время у меня есть факториал, но я должен сделать его обратимым. Пожалуйста помогите.


person sonchi    schedule 26.09.2013    source источник
comment
Я добавил пример.   -  person Sergei Lodyagin    schedule 26.09.2013


Ответы (4)


Предикаты Пролога - это отношения, поэтому, как только вы определили факториал, вы неявно определили и обратное. Однако в Прологе модифицируется регулярная арифметика, то есть все выражение в (is)/2 или (>)/2 должно быть известно во время выполнения, и если это не так, возникает ошибка. Ограничения преодолевают этот недостаток:

:- use_module(library(clpfd)).

n_factorial(0, 1).
n_factorial(N, F) :-
   N #> 0, N1 #= N - 1, F #= N * F1,
   n_factorial(N1, F1).

Это определение теперь работает в обоих направлениях.

?- n_factorial(N,6).
N = 3 ;
false.

?- n_factorial(3,F).
F = 6 ;
false.

Начиная с SICStus 4.3.4 и SWI 7.1.25 также завершается следующее:

?- n_factorial(N,N).
   N = 1
;  N = 2
;  false.

Дополнительную информацию см. В руководстве.

person false    schedule 26.09.2013
comment
@ложный. С clpfd в SWI запрос действительно завершается. Почему? - person repeat; 04.09.2015
comment
@ложный. Я копирую это, но с ?- n_factorial(N,N). я получаю N = 1 ; N = 2 ; false. На самом деле я действительно получил прерывание с каким-то другим вариантом n_fac, который я закодировал в другом месте при выполнении запроса ?- n_fac(N,N), false. ... но здесь? Нет. - person repeat; 04.09.2015
comment
@ложный. Хорошо знать! Конечно, я не сомневался, что ваше наблюдение было верным, когда вы его сделали. Я просто не мог это воспроизвести. Так что спасибо за дополнительные усилия по исследованию нескольких версий. - person repeat; 04.09.2015
comment
Это определение работает в обоих направлениях и является наиболее естественным и читаемым способом его реализации, но оно очень медленное при поиске обратного факториала, например n_factorial(Y, 1000000000) на ответ false на моем компьютере требуется около 20 секунд. - person Fatalize; 18.11.2016
comment
@Fatalize: Согласен! Есть много способов улучшить программы CLP (FD)! Дело в том, что нам следует начать с неверного определения или с декларативного, но медленного? - person false; 18.11.2016
comment
n_factorial(N,N). должен завершиться в SICStus Prolog 4.3.4, который был выпущен вчера. - person Per Mildner; 29.11.2016

Для справки, вот лучшая реализация декларативного предиката factorial, которую я мог придумать.

Два основных момента отличаются от ответа @ false:

  • Он использует аргумент аккумулятора, а рекурсивные вызовы увеличивают коэффициент, на который мы умножаем факториал, вместо стандартной рекурсивной реализации, в которой базовым случаем является 0. Это делает предикат намного быстрее, когда факториал известен, а начальное число - нет.

  • Он широко использует if_/3 и (=)/3 из модуля _5 _, чтобы по возможности избавиться от ненужных точек выбора. Он также использует (#>)/3 и овеществленное (===)/6, которое является вариацией (=)/3 для случаев, когда у нас есть две пары, которые можно использовать для if -> then части if_.

factorial/2

factorial(N, F) :-
    factorial(N, 0, 1, F).

factorial(N, I, N0, F) :-
    F #> 0,
    N #>= 0,
    I #>= 0,
    I #=< N,
    N0 #> 0,
    N0 #=< F,
    if_(I #> 2,
        (   F #> N,
            if_(===(N, I, N0, F, T1),
                if_(T1 = true,
                    N0 = F,
                    N = I
                ),
                (   J #= I + 1,
                    N1 #= N0*J,
                    factorial(N, J, N1, F)
                )
            )
        ),
        if_(N = I,
            N0 = F,
            (   J #= I + 1,
                N1 #= N0*J,
                factorial(N, J, N1, F)
            )
        )
    ).

(#>)/3

#>(X, Y, T) :-
    zcompare(C, X, Y),
    greater_true(C, T).

greater_true(>, true).
greater_true(<, false).
greater_true(=, false).

(===)/6

===(X1, Y1, X2, Y2, T1, T) :-
    (   T1 == true -> =(X1, Y1, T)
    ;   T1 == false -> =(X2, Y2, T)    
    ;   X1 == Y1 -> T1 = true, T = true
    ;   X1 \= Y1 -> T1 = true, T = false
    ;   X2 == Y2 -> T1 = false, T = true
    ;   X2 \= Y2 -> T1 = false, T = false
    ;   T1 = true, T = true, X1 = Y1
    ;   T1 = true, T = false, dif(X1, Y1)
    ).

Некоторые запросы

?- factorial(N, N).
N = 1 ;
N = 2 ;
false.          % One could probably get rid of the choice point at the cost of readability


?- factorial(N, 1).
N = 0 ;
N = 1 ;
false.          % Same


?- factorial(10, N).
N = 3628800.    % No choice point


?- time(factorial(N, 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000)).
% 79,283 inferences, 0.031 CPU in 0.027 seconds (116% CPU, 2541106 Lips)
N = 100.        % No choice point


?- time(factorial(N, 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518284253697920827223758251185210916864000000000000000000000000)).
% 78,907 inferences, 0.031 CPU in 0.025 seconds (125% CPU, 2529054 Lips)
false.


?- F #> 10^100, factorial(N, F).
F = 11978571669969891796072783721689098736458938142546425857555362864628009582789845319680000000000000000,
N = 70 ;
F = 850478588567862317521167644239926010288584608120796235886430763388588680378079017697280000000000000000,
N = 71 ;
F = 61234458376886086861524070385274672740778091784697328983823014963978384987221689274204160000000000000000,
N = 72 ;
...
person Fatalize    schedule 18.11.2016
comment
До сих пор вы находили оптимизацию для простых итеративных переменных (в отсутствие лучшего названия) в SO только для более простых случаев, таких как list_length/2. Интересно, можно ли это как-нибудь автоматизировать! - person false; 18.11.2016
comment
@false Что вы имеете в виду под смешением арифметического и синтаксического равенства? Нет, насчет (#)/1 не знаю. - person Fatalize; 18.11.2016
comment
Пока X= 1+0, X#=1 успешно, X#=1, X= 1+0 терпит неудачу. Кажется, вам удалось устранить все подобные проблемы, что действительно удивительно. По крайней мере, в приведенном мной определении был такой недостаток: N = 1+1, n_factorial(N,2). успешно, но n_factorial(N,2),N = 1+1. терпит неудачу. Для преодоления этого есть функтор № / 1: #(X)#=1, X=0+1 завершается ошибкой, а X=0+1, #(X)#=1 - ошибкой. См. Руководство clpfd / clpz. Это все еще как-то предварительное, в идеале # было бы префиксным оператором с очень низким приоритетом, тогда можно было бы написать: #X + #Y #= #Z - person false; 18.11.2016
comment
@false Когда N заземлен, а F свободен, или когда оба свободны, то можно использовать (=)/3и делать N0 = F в части then. Но когда F заземлен, а N нет, тогда использование (=)/3 на N оставит точку выбора, в которой нет необходимости (последняя часть (=)/3 с dif), которую мы не получили бы, если бы F = N0 была частью if. Таким образом, (===)/6 позволяет объединить оба случая, сохраняя при этом правильное поведение, если и N, и F свободны. Добавление частей = и dif для X2, Y2 не требуется и фактически приведет к избыточным результатам. - person Fatalize; 18.11.2016
comment
В целом это if_ = or = ситуация, которая не является полностью симметричной (первая = также является ситуацией, когда обе стороны свободны). Имя === не очень информативное. Я согласен, я бы назвал его =;=, но это невозможно. - person Fatalize; 18.11.2016
comment
X1=2,===(X1,3,1,1,false,true) терпит неудачу, но ===(X1,3,1,1,false,true), X1=2. преуспевает. Следовательно, === /6 не является отношением. - person false; 18.11.2016
comment
Может тебе понадобится when((?=(X1,X2) ; ?=(Y1,Y2)), ... ) - person false; 18.11.2016
comment
@false Верно. Это можно исправить, добавив дополнительные случаи для T1 == true и T1 == false, потому что в этом случае мы возвращаемся к реализации (=)/3. Поскольку я не думал о ситуации, когда мы вызываем (===)/6 с T1 землей, я не реализовал это. - person Fatalize; 18.11.2016
comment
Вам необходимо создавать декларативные строительные блоки. В остальном вы никоим образом не лучше чисто процедурных решений. - person false; 18.11.2016
comment
@false В идеале (===)/6 не должно даже существовать, и в этом случае будет использоваться модифицированная версия if_/3, так что нам не нужно использовать этот if_/3 в части then. - person Fatalize; 18.11.2016
comment
Я полагаю, вы имеете в виду (;)/3, а не if_/3. Но общая проблема даже более фундаментальна, чем это - person false; 18.11.2016
comment
Разве вы не хотите очистить свой раствор? Самый дешевый способ - использовать ошибки создания экземпляров! - person false; 24.11.2016
comment
@false Я улучшил (===)/6 реализацию, чтобы исправить проблему, о которой вы упомянули. - person Fatalize; 24.11.2016
comment
X1 = 1, ===(X1,3,1,1,T,true),T=false. терпит неудачу, но ===(X1,3,1,1,T,true),T=false, X1=1. преуспевает. Безотносительный. - person false; 24.11.2016
comment
@false Это потому, что за (===)/6 должен следовать if_(T1 = true, X2 = Y2, X1 = Y1), и в этом случае он не работает правильно в обоих случаях. Как я сказал ранее, в идеале (===)/6 не должно существовать, и следует предложить расширение if_/3 для прямого включения этой второй части, которой нет в (===)/6. Что-то вроде ifeq_or_ifeq(X1, Y1, X2, Y2, (then) X2 = Y2, (or then) X1 = Y1, (else)). - person Fatalize; 24.11.2016
comment
Вы можете поймать что-то также с помощью uninstantiation_error, если хотите. Но ваши предположения легко разбиваются. Смысл if_/3 в том, что он обеспечивает 100% чистый строительный блок, а не тот, который работает только в очень специфических ситуациях. - person false; 24.11.2016
comment
Действительно впечатляюще! - person repeat; 21.01.2020

простой "низкотехнологичный" способ: перечислить целые числа до тех пор, пока

  • вы найдете искомый факториал, затем "верните" число
  • строящийся факториал больше целевого. Тогда вы можете потерпеть неудачу ...

На практике вы можете просто добавить 2 аргумента к существующей факториальной реализации: цель и найденный обратный.

person CapelliC    schedule 26.09.2013
comment
что вы думаете о моем решении? - person Sergei Lodyagin; 26.09.2013
comment
@SergeiLodyagin: будет зацикливаться, если XFact не факториал. Добавьте тест перед рекурсивным вызовом. - person CapelliC; 26.09.2013

Просто реализуйте факториал (X, XFact), а затем замените аргументы

factorial(X, XFact) :- f(X, 1, 1, XFact).

f(N, N, F, F) :- !.
f(N, N0, F0, F) :- succ(N0, N1), F1 is F0 * N1, f(N, N1, F1, F).
person Sergei Lodyagin    schedule 26.09.2013