Prolog 将电影放在dvd上,但有风格/代码问题
我制作了一个prolog程序,向我展示在dvd上安装东西的最佳方式。问题在代码注释中,我将在下面粘贴以供参考,但归结起来是:Prolog 将电影放在dvd上,但有风格/代码问题,prolog,knapsack-problem,Prolog,Knapsack Problem,我制作了一个prolog程序,向我展示在dvd上安装东西的最佳方式。问题在代码注释中,我将在下面粘贴以供参考,但归结起来是: 是否有一种反向切割操作符使其搜索更多,尽管它已经匹配?参见fitexact,类似fitexact(大小,总和,L,L):-Sum 跟踪已处理电影的最佳方法是什么?我收回它们,但不知道没有它怎么做 fitfuzzy使用如果则构造。我不知道该怎么看他们,在序言中感觉很奇怪。然而,试图让它递归让我感到非常困惑:) %给你一张电影和尺寸的清单,试着把它们都放在dvd上 %尽可能少
如果
则
构造。我不知道该怎么看他们,在序言中感觉很奇怪。然而,试图让它递归让我感到非常困惑:)findall
就是您想要的只是一个一般性的评论:与跟踪处理过的电影相比,我发现首先获取(例如,通过findall/3)仍需要处理的电影列表,然后简单地将该列表处理掉更为自然。因此,您有了
burn_dvd(List0,dvd,List)
,它将电影列表(可能与它们的大小结合在一起,比如以movie_size(Name,size)
的形式)作为第一个参数,构造一张dvd(通过从列表0中选择尽可能多的电影放在一张dvd上,例如在按大小排序列表之后),第三个参数是剩余电影的列表。然后,您就有了一个自然扩展burn_DVD(列表,DVD),它可以简单地构造DVD,直到不再有电影保留:
burn_dvds([], []) :- !.
burn_dvds(Movies0, [DVD|DVDs]) :-
burn_dvd(Movies0, DVD, Movies),
burn_dvds(Movies, DVDs).
不需要断言/1或收回/1。如果burn_dvd/3非确定性地构造一张dvd,则可能有多种解决方案,这是您可能想要的,也是很自然的
但是,使用if-then-else是完全可以的,可以通过模式匹配来表示的所有内容都应该通过模式匹配来表示,因为它通常会生成更通用、更高效的代码
format/2也可以帮助您进行输出:而不是:
write('Movies left: '), write(MoviesLeft), nl
你可以写:
format("Movies left: ~w\n", [MoviesLeft])
通常,很少需要手动输出,因为您可以随时让顶级打印解决方案。在我们的例子中,当您查询时,burn_DVD/2自然会发出DVD列表作为其答案。Prolog的“最佳实践规则”指出,除非绝对需要断言/收回(即没有声明性方法),否则应避免断言/收回 这里有一个使用select/3生成所有组合的程序
movie(a, 10).
movie(b, 3).
movie(c, 5).
movie(d, 6).
movie(e, 10).
dvdsize(20).
burn(Best) :-
findall(N-S, movie(N,S), L),
dvdsize(Max),
setof(Wasted-Sequence, fitmax(L, Max, Wasted, Sequence), All),
All = [Best|_],
maplist(writeln, All).
fitmax(L, AvailableRoom, WastedSpace, [Title|Others]) :-
select(Title - MovieSize, L, R),
MovieSize =< AvailableRoom,
RoomAfterMovie is AvailableRoom - MovieSize,
fitmax(R, RoomAfterMovie, WastedSpace, Others).
fitmax(_, WastedSpace, WastedSpace, []).
我以前的回答是“快而脏”,随着电影数量的增加,很快就显示出它的局限性。这里有一个更好的方法来找到最合适的答案,并与之前的答案(根据测试要求重新制定)进行比较 优化的关键是标签背包,Axel在发布问题时正确使用了标签背包。我在CLP(FD)support中搜索了一种解决此问题的适当方法,如下所示:
:- [library(clpfd)].
%% use CLP(FD) to find best fit
%
burn_knapsack(Best, Wasted) :-
dvdsize(Max),
findall(Title - Size, movie(Title, Size), Movies),
knaps(Movies, Max, Best, Wasted).
knaps(Movies, Max, Best, Wasted) :-
findall([Flag, Title, Size],
(Flag in 0..1, member(Title - Size, Movies)), AllMovies),
transpose(AllMovies, [ToBurn, Titles, Sizes]),
Actual #=< Max,
scalar_product(Sizes, ToBurn, #=, Actual),
labeling([max(Actual)], [Actual|ToBurn]),
findall(Title, (nth1(I, ToBurn, 1),
nth1(I, Titles, Title)), Best),
Wasted is Max - Actual.
%% compute all combinations of movies that fit on a dvd
% it's a poor man clpfd:scalar_product/4
%
burn_naive(Best, Wasted) :-
dvdsize(Max),
findall(Title - Size, movie(Title, Size), Movies),
naive(Movies, Max, Best, Wasted).
naive(Movies, Max, Best, Wasted) :-
setof(Wasted-Sequence, fitmax(Movies, Max, Wasted, Sequence), [Wasted-Best|_]).
fitmax(L, AvailableRoom, WastedSpace, [Title|Others]) :-
select(Title - MovieSize, L, R),
MovieSize =< AvailableRoom,
RoomAfterMovie is AvailableRoom - MovieSize,
fitmax(R, RoomAfterMovie, WastedSpace, Others).
fitmax(_, WastedSpace, WastedSpace, []).
%% run test with random generated list
%
% From,To are num.of.movies
% SzMin, SzMax min/max+1 of each movie size
%
test_performance(From, To, DvdSize, SzMin, SzMax) :-
forall((between(From, To, NumMovies),
gen_movies(NumMovies, SzMin, SzMax, Movies)
),
( ( NumMovies < 11
-> test(naive, Movies, DvdSize)
; true
),
test(knaps, Movies, DvdSize)
)).
test(Method, Movies, DvdSize) :-
time(once(call(Method, Movies, DvdSize, Best, Wasted))),
writeln((Method, Best, Wasted)).
gen_movies(NumMovies, SzMin, SzMax, Movies) :-
findall(Title-Size,
( between(1, NumMovies, Title),
random(SzMin, SzMax, Size)
), Movies).
好的,我应该列一个清单,谢谢你指出这一点。关于多个解决方案,您是指已创建dvd的排列吗?如果可能的话,我想避免这种情况,这就是我收回它们的原因。不仅是排列,而且显然有很多方法可以制作出单张DVD,但仍然会导致非同构的解决方案。例如,当两部电影大小完全相同时,您可以将其中一部或另一部放在两张仍有足够空间的DVD上。使用诸如assert/1和retract/1之类的副作用会妨碍良好的声明性解决方案,使您可以通过使用Prolog的自然搜索策略轻松探索这些不同的变体。是的,我想我在同一个函子中使用retract时注意到了这些副作用,请参见注释
?- burn(X).
0-[a,e]
0-[e,a]
1-[a,b,d]
1-[a,d,b]
1-[b,a,d]
1-[b,d,a]
1-[b,d,e]
1-[b,e,d]
1-[d,a,b]
1-[d,b,a]
1-[d,b,e]
1-[d,e,b]
1-[e,b,d]
1-[e,d,b]
2-[a,b,c]
2-[a,c,b]
2-[b,a,c]
2-[b,c,a]
2-[b,c,e]
2-[b,e,c]
2-[c,a,b]
2-[c,b,a]
2-[c,b,e]
2-[c,e,b]
2-[e,b,c]
2-[e,c,b]
4-[a,d]
4-[d,a]
4-[d,e]
4-[e,d]
5-[a,c]
5-[c,a]
5-[c,e]
5-[e,c]
6-[b,c,d]
6-[b,d,c]
6-[c,b,d]
6-[c,d,b]
6-[d,b,c]
6-[d,c,b]
7-[a,b]
7-[b,a]
7-[b,e]
7-[e,b]
9-[c,d]
9-[d,c]
10-[a]
10-[e]
11-[b,d]
11-[d,b]
12-[b,c]
12-[c,b]
14-[d]
15-[c]
17-[b]
20-[]
X = 0-[a, e].
:- [library(clpfd)].
%% use CLP(FD) to find best fit
%
burn_knapsack(Best, Wasted) :-
dvdsize(Max),
findall(Title - Size, movie(Title, Size), Movies),
knaps(Movies, Max, Best, Wasted).
knaps(Movies, Max, Best, Wasted) :-
findall([Flag, Title, Size],
(Flag in 0..1, member(Title - Size, Movies)), AllMovies),
transpose(AllMovies, [ToBurn, Titles, Sizes]),
Actual #=< Max,
scalar_product(Sizes, ToBurn, #=, Actual),
labeling([max(Actual)], [Actual|ToBurn]),
findall(Title, (nth1(I, ToBurn, 1),
nth1(I, Titles, Title)), Best),
Wasted is Max - Actual.
%% compute all combinations of movies that fit on a dvd
% it's a poor man clpfd:scalar_product/4
%
burn_naive(Best, Wasted) :-
dvdsize(Max),
findall(Title - Size, movie(Title, Size), Movies),
naive(Movies, Max, Best, Wasted).
naive(Movies, Max, Best, Wasted) :-
setof(Wasted-Sequence, fitmax(Movies, Max, Wasted, Sequence), [Wasted-Best|_]).
fitmax(L, AvailableRoom, WastedSpace, [Title|Others]) :-
select(Title - MovieSize, L, R),
MovieSize =< AvailableRoom,
RoomAfterMovie is AvailableRoom - MovieSize,
fitmax(R, RoomAfterMovie, WastedSpace, Others).
fitmax(_, WastedSpace, WastedSpace, []).
%% run test with random generated list
%
% From,To are num.of.movies
% SzMin, SzMax min/max+1 of each movie size
%
test_performance(From, To, DvdSize, SzMin, SzMax) :-
forall((between(From, To, NumMovies),
gen_movies(NumMovies, SzMin, SzMax, Movies)
),
( ( NumMovies < 11
-> test(naive, Movies, DvdSize)
; true
),
test(knaps, Movies, DvdSize)
)).
test(Method, Movies, DvdSize) :-
time(once(call(Method, Movies, DvdSize, Best, Wasted))),
writeln((Method, Best, Wasted)).
gen_movies(NumMovies, SzMin, SzMax, Movies) :-
findall(Title-Size,
( between(1, NumMovies, Title),
random(SzMin, SzMax, Size)
), Movies).
?- test_performance(8,20, 30, 3,7).
% 93,155 inferences, 0,140 CPU in 0,140 seconds (100% CPU, 665697 Lips)
naive,[1,2,3,5,6],0
% 235,027 inferences, 0,159 CPU in 0,159 seconds (100% CPU, 1481504 Lips)
knaps,[2,3,5,6,8],0
% 521,369 inferences, 0,782 CPU in 0,783 seconds (100% CPU, 666694 Lips)
naive,[1,2,3,4,5,6],0
% 163,858 inferences, 0,130 CPU in 0,131 seconds (100% CPU, 1255878 Lips)
knaps,[3,4,5,6,7,9],0
% 1,607,675 inferences, 2,338 CPU in 2,341 seconds (100% CPU, 687669 Lips)
naive,[1,2,3,4,7,8],0
% 184,056 inferences, 0,179 CPU in 0,180 seconds (100% CPU, 1027411 Lips)
knaps,[5,6,7,8,9,10],0
% 227,510 inferences, 0,156 CPU in 0,156 seconds (100% CPU, 1462548 Lips)
knaps,[5,6,8,9,10,11],0
% 224,621 inferences, 0,155 CPU in 0,155 seconds (100% CPU, 1451470 Lips)
knaps,[6,7,8,9,10,11,12],0
% 227,591 inferences, 0,159 CPU in 0,159 seconds (100% CPU, 1434836 Lips)
knaps,[5,7,9,10,11,12,13],0
% 389,764 inferences, 0,263 CPU in 0,263 seconds (100% CPU, 1482017 Lips)
knaps,[5,8,9,10,12,13,14],0
% 285,944 inferences, 0,197 CPU in 0,198 seconds (100% CPU, 1448888 Lips)
knaps,[8,9,10,12,13,14,15],0
% 312,936 inferences, 0,217 CPU in 0,217 seconds (100% CPU, 1443891 Lips)
knaps,[10,11,12,14,15,16],0
% 343,612 inferences, 0,238 CPU in 0,238 seconds (100% CPU, 1445670 Lips)
knaps,[12,13,14,15,16,17],0
% 403,782 inferences, 0,277 CPU in 0,278 seconds (100% CPU, 1456345 Lips)
knaps,[11,12,13,15,16,17],0
% 433,078 inferences, 0,298 CPU in 0,298 seconds (100% CPU, 1455607 Lips)
knaps,[14,15,16,17,18,19],0
% 473,792 inferences, 0,326 CPU in 0,327 seconds (100% CPU, 1451672 Lips)
knaps,[14,15,16,17,18,19,20],0
true.