【问题标题】:Prolog last element function returns whole listProlog最后一个元素函数返回整个列表
【发布时间】:2021-10-14 21:35:34
【问题描述】:

我有一个任务是在 Prolog 中实现快速排序,然后返回最后一个元素。我有快速排序工作,但是当它到达最后一个元素调用时,它会返回整个列表。我的最后一个元素在单独运行时有效,但在我在快速排序结束时调用它时无效。关于我需要改变什么的任何想法?

quicksort([],[]).
quicksort([H|T],Final) :- 
    partition(T,H,Left,Right), 
    quicksort(Left,Ls),                           
    quicksort(Right,Rs), 
    append(Ls,[H|Rs],Sorted), 
    lastelement(Final, [Sorted]).

partition([],Pivot,[],[]).
partition([H|T],Pivot,[H|Ls],Rs) :- H =< Pivot, partition(T,Pivot,Ls,Rs).
partition([H|T],Pivot,Ls,[H|Rs]) :- H > Pivot, partition(T,Pivot,Ls,Rs).

append([],Sorted,Sorted).
append([H|T],Sorted,[H|Z]) :- append(T,Sorted,Z).

lastelement(Final, [Final]).
lastelement(Final, [_|T]):- lastelement(Final,T).

【问题讨论】:

  • 去掉调用lastelement(Final, [Sorted])的括号
  • 我试过了,但它返回 false
  • 那么在调用lastelement之前你可能还有另一个问题。您确定您从通话中删除了括号吗?它应该是lastelement(Final, Sorted)
  • 是的,我删除了方括号,所以它的读取方式与此完全相同,但返回 false。
  • 那么你应该检查你程序的其他部分。 quicksort 的第二个参数是第一个子句的列表和第二个子句的项目。我认为您将作业的两个部分混合在一起。首先计算快速排序,它应该有一个列表作为第二个参数。然后调用lastelement获取最后一个元素

标签: prolog


【解决方案1】:

正如@gusbro 所观察到的,不可能定义一个递归谓词,其基本情况 生成一个列表,而其递归步骤 生成一个元素。因此,一个可能的解决方案是:

quicksort([],[]).
quicksort([H|T], Sorted) :-
    mypartition(T, H, Left, Right),
    quicksort(Left,  Ls),
    quicksort(Right, Rs),
    myappend(Ls, [H|Rs], Sorted).

mypartition([], _Pivot,[],[]).
mypartition([H|T],Pivot,[H|Ls],Rs) :- H =< Pivot, mypartition(T,Pivot,Ls,Rs).
mypartition([H|T],Pivot,Ls,[H|Rs]) :- H > Pivot, mypartition(T,Pivot,Ls,Rs).

myappend([], Sorted, Sorted).
myappend([H|T], Sorted, [H|Z]) :- myappend(T, Sorted, Z).

lastelement(Final, [Final]).
lastelement(Final, [_|T]):- lastelement(Final,T).

quicksort_last(List, Last) :-
    quicksort(List, Sorted),
    lastelement(Last, Sorted).

例子:

?- quicksort([61,46,59,27,12,38], S).
S = [12, 27, 38, 46, 59, 61] ;
false.

?- quicksort_last([61,46,59,27,12,38], S).
S = 61 ;
false.

更好的解决方案

quicksort_last/2 的平均复杂度时间为 O(n lg n)。假设您的解决方案必须基于快速排序的思想,这是一种更有效的方法,平均复杂度时间为 O(n),是:

  • 要拥有最后一项(又名,最大项),完整的排序列表不能为空。
  • 分区后:
    • 如果右子列表为空,则 Pivot 是完整排序列表中的最后一个元素(因为 Pivot 比 left 子列表中的所有元素更大
    • 否则,右子列表的最后一个元素也是完整排序列表的最后一个元素(因为,如果子列表中至少有一项,那么Pivot就是比它小)。
quick_last([Pivot|Rest], Last) :-
    mypartition(Rest, Pivot, _, Right),
    (   Right = []
    ->  Last = Pivot
    ;   quick_last(Right, Last) ).

例子:

?- quick_last([61,46,59,27,12,38], S).
S = 61 ;
false.

?- quick_last([71, 100, 83, 97, 62, 6, 42, 3, 40], L).
L = 100 ;
false.

在最好的情况下,每次递归调用quick_last/2,列表的长度除以2。由于partition/4消耗的时间与列表的长度成正比,我们有T(n) = n + n/2 + n/4 + ... + 1 ≈ 2*n。

REMARK 此解决方案是一种算法的特例,用于在无序列表中查找第 k 个最小元素(请参阅:quick-select)。

【讨论】:

  • 如果您只想要列表的最大数量而不是quick_last/2,您可能只需要通过列表保持找到的最大数量,无需为此进行任何排序。
  • @gusbro 当然是这样(其实很明显),但是如果你只需要一个有序列表的最后一个元素,你也不需要排序就可以找到找出那个元素是什么。在我的回答中,我假设这是一个具有教学目标的问题,其目的只是为了适应快速排序算法的思想来解决另一个问题。
  • @gusbro 我不知道你有没有注意到,但是 OP 是创建一个基于快速排序的谓词,它对列表进行排序并只返回排序列表的最后一个元素(所以排序的工作整个列表是不必要的,但使用快速排序不是)。
  • @slago 非常感谢您的解决方案有效,不敢相信我看不到这一点。你是对的,对列表进行排序然后只关心最后一个元素是没有意义的,但这就是分配。
【解决方案2】:

lastElement/2 似乎试图从排序列表中找到最后一个元素......并将其作为列表本身在各种递归调用中传回。例如,quicksort(Left,Ls) 被称为好像 Ls 将是 Left 的排序版本,但它实际上返回该列表的最后一个元素。

如果你想要最后一个元素,当然可以,但你可以在完成快速排序后得到它。 (或者可以通过其他方式找到它。)

quicksort([H|T],Sorted) :- partition(T,H,Left,Right), 
                           quicksort(Left,Ls),
                           quicksort(Right,Rs), 
                           append(Ls,[H|Rs],Sorted).

findLastElement(List,Final) :- quicksort(List,Sorted), lastelement(Final, Sorted).

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-21
    • 2016-08-09
    • 2023-04-02
    • 2016-01-30
    • 1970-01-01
    • 1970-01-01
    • 2021-03-11
    • 1970-01-01
    相关资源
    最近更新 更多