Самая длинная подпоследовательность в прологе
Я хочу реализовать предикат P(Xs,Ys,Zs)
где Xs
,Ys
,Zs
являются списками.
Я новичок в Прологе и не могу найти способ добраться до самой длинной последовательности в Xs
(пример. Xs = ['b','b','A','A','A','A','b','b']
) который включен в Ys
(например Ys = ['A','A','A','A','c','A','A','A','A']
без пересечения - четное количество раз. Может быть, кто-то уже написал этот код или кто-то может сказать мне, как я могу начать. Спасибо за помощь.
объяснение учителя.
longest_subsequence(List, Part, Subsequence):-
longest_subsequence_(List, Part, [], Subsequence).
longest_subsequence_(Xs, Ys, CurrentSubsequence, LongestSubsequence):-
append(CurrentSubsequence, Ys, NextSubsequence),
divide_list(Xs, [_LeftYs, NextSubsequence, _RightYs]), !,
longest_subsequence_(Xs, Ys, NextSubsequence, LongestSubsequence).
longest_subsequence_(_Xs, _Ys, LongestSubsequence, LongestSubsequence).
3 ответа
Хорошо, я сделал.
main_task(Xs, Ys, Zs) :-
atom_chars(Xs, Xl),
atom_chars(Ys, Yl),
retractall(record(_, _)),
assert(record(0, [])),
process(Xl, Yl, Zl),
atom_chars(Zs, Zl).
process(Xl, Yl, _) :-
get_sublist(Xl, Zl),
length(Zl, L),
record(MaxL, _),
L > MaxL,
get_index(Yl, Zl, Il),
test_even(Il),
test_intersect(Il, L),
retractall(record(_, _)),
assert(record(L, Zl)),
fail.
process(_, _, Zl) :-
record(_, Zl).
get_sublist(L1, L2) :-
get_tail(L1, L3),
get_head(L3, L2).
get_tail(L, L).
get_tail([_|T], L) :-
get_tail(T, L).
get_head([H|T1], [H|T2]) :-
get_head(T1, T2).
get_head(_, []).
get_index(Yl, Zl, Il) :-
get_index(Yl, Zl, Il, 0).
get_index([], _, [], _).
get_index([Yh|Yt], Zl, [I|It], I) :-
get_head([Yh|Yt], Zl),
!,
I1 is I + 1,
get_index(Yt, Zl, It, I1).
get_index([_|Yt], Zl, Il, I) :-
I1 is I + 1,
get_index(Yt, Zl, Il, I1).
test_even(Il) :-
length(Il, L),
L > 0,
L mod 2 =:= 0.
test_intersect([_], _).
test_intersect([X,Y|T], L) :-
Y - X >= L,
test_intersect([Y|T], L).
- Все строки в списке по символам при работе со списками
- Инициализировать динамическую базу данных - в ней будет храниться максимальная длина строки
- перечисляет все подстроки (подсписки) из X. Бюст проходит двойную "обрезку" - сначала в списке обрезанных спереди, затем сзади.
- Проверьте длину полученной строки, если у нас уже есть длинная, немедленно оставьте для продолжения перебора
- Мы рассматриваем список индексов в появлении Y, тогда есть каждый элемент списка - позиция в Y, из которой он включает Z.
- Проверьте четность - просто рассмотрите длину списка индексов, чётная длина - четное количество записей. И нам нужно проверить, что оно больше нуля.
- Проверьте пересечение - вам нужно проверить разницу между двумя смежными элементами списка индексов, разница всегда должна быть больше длины Z.
- Если все проверки сделаны, происходит динамическое обновление базы данных - текущий список Z сохраняется как максимум
- В конце концов это вынужденный сбой, он откатывается до развилки в пункте 3) и продолжается поиск. Примечание: Если какая-либо проверка не выполняется, сбой этого теста немедленно откатывается до развилки в параграфе 3) и продолжается поиск.
- Когда бюст подходит к концу, выполняется второй процесс предиката правила, он просто выбирает последний всплеск Z в базе.
- В конце списка Z преобразуется обратно в строку
При приближении к проблеме сначала попробуйте: разделяй и властвуй.
Когда у нас есть list_subsequence(+List, ?Subsequence)
сказуемое
list_subsequence([H|T], S) :-
list_subsequence(H, T, S, _).
list_subsequence([H|T], S) :-
list_subsequence(H, T, _, R),
list_subsequence(R, S).
list_subsequence(H, [H|T], [H|S], R) :- !, list_subsequence(H, T, S, R).
list_subsequence(H, R, [H], R).
мы можем вызвать библиотечную ( совокупную) помощь:
longest_subsequence(Seq, Rep, Longest) :-
aggregate(max(L, Sub), N^(
list_subsequence(Seq, Sub),
aggregate(count, list_subsequence(Rep, Sub), N),
N mod 2 =:= 0,
length(Sub, L)
), max(_, Longest)).
Наивный подход заключается в следующем:
longest_subsequence(Xs,Ys,Zs) :-
longest_subsequence(Xs,Ys,Ys,0,[],Zs).
longest_subsequence([X|Xs],Y0,[Y|Ys],N0,Z0,Z) :-
try_seq([X|Xs],[Y|Ys],Nc,Zc),
(Nc > N0
-> longest_subsequence([X|Xs],Y0,Ys,Nc,Zc,Z)
; longest_subsequence([X|Xs],Y0,Ys,N0,Z0,Z)
).
longest_subsequence([_|Xs],Y0,[],N0,Z0,Z) :-
longest_subsequence(Xs,Y0,Y0,N0,Z0,Z).
longest_subsequence([],_,_,_,Z,Z).
try_seq([H|TA],[H|TB],N,[H|TC]) :-
!,
try_seq(TA,TB,N1,TC),
N is N1+1.
try_seq(_,_,0,[]).
здесь предикат try_seq/3
стремится максимально соответствовать (генерирует самую длинную общую подпоследовательность), начиная с начала списка.
Проблема заключается в том, что это вычислительно дорогой подход: он будет иметь временную сложность O(m n p) с n длиной первого списка, m длиной второго списка и p минимальной длиной двух списков.
Вызов этого в вашем примере дает:
?- longest_subsequence([b,b,a,a,a],[a,a,a,c,a,a,a],Zs).
Zs = [a, a, a] ;
false.
Вы можете сделать алгоритм более эффективным, используя обратные ссылки, более или менее основанные на алгоритме Кнута-Морриса-Пратта.