Пролог - получить факторы для данного числа не останавливается?
Мне нужно найти факторы данного числа, например:
?- divisors2(40,R).
R = [40,20,10,8,5,4,2,1].
Код:
% get all the numbers between 1-X
range(I,I,[I]).
range(I,K,[I|L]) :- I < K, I1 is I + 1, range(I1,K,L).
% calc the modulo of each element with the given number :
% any x%y=0 would be considered as part of the answer
divisors1([],[],_).
divisors1([H|T],S,X):-divisors1(T,W,X),Z is X mod H,Z==0,S=[H|W].
divisors1([_|T],S,X):-divisors1(T,S,X).
divisors2(X,Result) :-range(1,X,Result1),divisors1(Result1,Result,X).
Но когда я бегу divisors2(40,RR).
Я получаю бесконечный цикл, и на экране ничего не отображается.
Зачем?
С уважением
3 ответа
У вас есть ошибка здесь
divisors1([H|T],S,X):-
divisors1(T,W,X),
Z is X mod H,
Z==0,S=[H|W]. <=== here
Если Z равен нулю, то S = [H|W], иначе S = W.
Вы спрашиваете, почему вы получаете бесконечный цикл для запроса divisors2(40,R)
, Я почти хотел объяснить вам это с помощью среза ошибки. Увы...
... ответ: нет, вы не получите бесконечный цикл! И ваша программа тоже находит ответ. Это
R = [1, 2, 4, 5, 8, 10, 20, 40]
что выглядит разумным для меня. Они расположены в порядке возрастания, и вы хотели, чтобы список по убыванию, но кроме этого, это идеальный ответ. Без шуток. Однако я подозреваю, что вы недостаточно терпеливы, чтобы получить ответ. Для 36 мне понадобилось:
?- time(divisors2(36,R)).
% 10,744,901,605 inferences, 2248.800 CPU in 2252.918 seconds (100% CPU, 4778061 Lips)
R = [1, 2, 3, 4, 6, 9, 12, 18, 36]
Довольно необычно... для списка с не более чем 36 скудными целыми числами Прологу потребовалось 10 744 901 605 выводов, то есть менее 2 34. Это звонит в колокол? В любом случае, есть проблемы с вашей программой. На самом деле есть две совершенно самостоятельные проблемы. Как мы можем их найти?
Может быть, мы смотрим не на ту сторону. Просто вернитесь к запросу. Нашей первой ошибкой было то, как мы использовали уровень Пролога. Мы были очень впечатлены, чтобы получить ответ. Но Пролог предложил нам дальнейшие ответы! По факту:
?- time(divisors2(36,R)).
% 10,744,901,605 inferences, 2248.800 CPU in 2252.918 seconds (100% CPU, 4778061 Lips)
R = [1, 2, 3, 4, 6, 9, 12, 18, 36] ;
% 10 inferences, 0.000 CPU in 0.000 seconds (82% CPU, 455892 Lips)
R = [1, 2, 3, 4, 6, 9, 12, 18] ;
% 917,508 inferences, 0.192 CPU in 0.192 seconds (100% CPU, 4789425 Lips)
R = [1, 2, 3, 4, 6, 9, 12, 36] ...
Это становится слишком утомительным. Может быть, достаточно крошечного примера?
?- divisors2(6,R).
R = [1, 2, 3, 6] ;
R = [1, 2, 3] ;
R = [1, 2, 6] ;
R = [1, 2] ;
R = [1, 3, 6] ;
R = [1, 3] ;
R = [1, 6] ;
R = [1] ;
R = [2, 3, 6] ;
R = [2, 3] ;
R = [2, 6] ;
R = [2] ;
R = [3, 6] ;
R = [3] ;
R = [6] ;
R = [] ;
false.
Более, чем достаточно! Может быть, мы придерживаемся минимального примера []
и повторим это:
?- divisors2(6,[]).
true ;
false.
Понятно, что это не то, что мы ожидали. Мы хотели, чтобы это потерпело неудачу. Как локализовать проблему? В Прологе есть одна общая стратегия отладки:
Если цель слишком общая, специализируйте программу.
Мы можем специализировать программу, добавляя дополнительные цели, чтобы вышеуказанный запрос все еще был успешным. Я добавлю false
и немного (=)/2
цели. false
Это особенно интересно, потому что он стирает весь пункт:
? - делители2(6,[]). диапазон (I, I, [I]): - I = 6. диапазон (I, K, [I | L]): - K = 6, Я <К, I1 это I + 1, Диапазон (I1, K, L). делители1 ([], [], X): - K = 6.делители1([H|T],S,X):- неверно,divisors1 (Т,W, Х),Z является X mod H,Z = 0,S = [H | W].делители1 ([_ | T], S, X): - S = [], X = 6, divisors1(Т,S,X). делители2(X, Результат): - X = 6, Результат = []. Диапазон (1, Х,Result1), divisors1(Result1, результат, X).
Где-то в оставшейся части что-то слишком общее! На самом деле рекурсивное правило divisors1/3
слишком общий. Эта ваша новая модифицированная программа называется ломтик, который является специализацией нашей оригинальной программы.
Несколько способов исправить это, самый наивный способ - добавить соответствующее условие следующим образом:
divisors1 ([], [], _). divisors1([H|T],S,X):- divisors1 (Т,W, Х), 0 =:= Х мод Н, S=[H|W]. divisors1([H|T],S,X):- divisors1(Т,S,X), 0 =\= Х мод Х.
Однако производительность программы не улучшилась. Чтобы увидеть это, я снова специализируюсь на этой программе:
делители1([],[],_):- неверно. divisors1([H|T],S,X):- делители1(T,W,X), ложь,0 =: = Х мод Н,S = [H | W]. divisors1([H|T],S,X):- делители1(T,S,X), ложь,0 = \ = Х мод Н.
Таким образом: независимо от того, что находится за false
эта программа попробует хотя бы 3 * 2^N
умозаключения для списка длины N
,
Ставя рекурсивные цели в последнюю очередь, мы можем избежать этого.
Если вы исправите свой диапазон (используйте сокращение в конце конца рекурсии), вы получите его вроде как работающий. Вам не сразу удастся найти все делители.
Решение, использующее вашу общую идею, а также встроенные функции между /3 и bagof/3 (чтобы немного упростить набор текста):
divisors(X, Divs) :- bagof(D, divs(X,D), Divs).
divs(X,D) :- between(1,X,D), 0 is X mod D.
Обратите внимание, что это решение возвращает делители в порядке возрастания.