Я думаю, что следующий код может быть полезен для вас. Я не доказал его правильность, но, похоже, он работает нормально (даже для очень больших входов). В любом случае, вы можете использовать его как отправную точку для правильной реализации.
shortest_length(List, K, ShortestLength) :-
shortest_sublist(List, K, Sublist),
length(Sublist, ShortestLength).
shortest_sublist(List, K, Sublist) :-
split(List, K, Prefix, Suffix), % find minimum prefix with all K items
shrink(Prefix, [First|Rest]), % shrink that prefix to get a sublist
append(Rest, Suffix, NewList),
( shortest_sublist(NewList, K, NewSublist) % find a new sublist in the rest of the list
-> ( length([First|Rest], Len1),
length(NewSublist, Len2),
( Len1 < Len2
-> Sublist = [First|Rest]) % new sublist is not shorter than the previous
; Sublist = NewSublist ) % new sublist is shorter than the previous
; Sublist = [First|Rest] ). % a new sublist was not found
split(List, K, Prefix, Suffix) :-
append(Prefix, Suffix, List),
has(Prefix, K), !.
has(List, K) :-
forall( between(1, K, Item),
memberchk(Item, List) ).
shrink([First|Rest], ShrunkList) :-
( memberchk(First, Rest)
-> shrink(Rest, ShrunkList)
; ShrunkList = [First|Rest] ).
Некоторые результаты для небольших вводов:
?- shortest_length([1,3,1,3,1,3,3,2,2,1], 3, N).
N = 4.
?- shortest_sublist([1,3,1,3,1,3,3,2,2,1], 3, S).
S = [3, 2, 2, 1].
?- shortest_sublist([1,3,1,3,1,3,3,2,2,1,3,3], 3, S).
S = [2, 1, 3].
Некоторые результаты для больших входов:
?- length(L, 500000), maplist(random(1,5),L), time(shortest_sublist(L, 4, S)).
% 11,153,796 inferences, 1.766 CPU in -712561273706905600.000 seconds (?% CPU, 6317194 Lips)
L = [2, 1, 3, 4, 2, 2, 4, 4, 3|...],
S = [4, 1, 2, 3].
?- length(L, 1000000), maplist(random(1,5),L), time(shortest_sublist(L, 4, S)).
% 22,349,463 inferences, 3.672 CPU in -657663431226163200.000 seconds (?% CPU, 6086662 Lips)
L = [2, 2, 4, 3, 2, 2, 3, 1, 2|...],
S = [2, 1, 4, 3].
?- length(L, 2000000), maplist(random(1,5),L), time(shortest_sublist(L, 4, S)).
% 44,655,878 inferences, 6.844 CPU in 919641833393356800.000 seconds (0% CPU, 6525060 Lips)
L = [4, 1, 3, 3, 4, 3, 3, 3, 2|...],
S = [2, 1, 3, 4].
Для малых значений K алгоритм, по-видимому, потребляет время, пропорциональное O (n). Обратите внимание, что, когда длина списка удваивается, время выполнения также удваивается (то есть 500000 → ~ 1,8 с, 1000000 → ~ 3,7 с, 2000000 → ~ 6,9 с).
Я думаю, что узкое место находится в предикате has/1
. Таким образом, для более эффективной реализации (для больших значений K) вам нужна более эффективная стратегия проверки членства в списке.
Улучшение: Я понял, что, когда найден подсписок длины K, мы можем заключить, что не существует более короткого списка, который бы содержал все элементы K, и, следовательно, мы можем остановить поиск. Таким образом, предикат shortest_sublist/3
можно изменить следующим образом:
shortest_sublist(List, K, Sublist) :-
split(List, K, Prefix, Suffix), % find minimum prefix with all K items
shrink(Prefix, [First|Rest]), % shrink that prefix to get a sublist
length([First|Rest], Len1),
( Len1 = K % there is no shorter sublist
-> Sublist = [First|Rest]
; append(Rest, Suffix, NewList),
( shortest_sublist(NewList, K, NewSublist) % find a new sublist in the rest of the list
-> ( length(NewSublist, Len2),
( Len1 < Len2
-> Sublist = [First|Rest]) % new sublist is not shorter than the previous
; Sublist = NewSublist ) % new sublist is shorter than the previous
; Sublist = [First|Rest] ) ). % a new sublist was not found
Вот некоторые результаты, полученные с помощью этой новой версии предиката:
?- shortest_sublist([1,3,1,3,1,3,3,2,2,1], 3, S).
S = [3, 2, 2, 1].
?- shortest_sublist([1,3,1,3,1,3,3,2,2,1,3,3], 3, S).
S = [2, 1, 3].
?- length(L, 500000), maplist(random(1,5), L), time(shortest_sublist(L, 4, S)).
% 1,041 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
L = [2, 3, 2, 2, 4, 1, 2, 1, 1|...],
S = [3, 2, 4, 1].
?- length(L, 1000000), maplist(random(1,5), L), time(shortest_sublist(L, 4, S)).
% 880 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
L = [3, 2, 1, 1, 4, 4, 3, 1, 1|...],
S = [1, 3, 2, 4].
?- length(L, 2000000), maplist(random(1,5), L), time(shortest_sublist(L, 4, S)).
% 125 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
L = [2, 1, 1, 4, 3, 2, 2, 2, 3|...],
S = [1, 4, 3, 2].