【问题标题】:Prolog, find minimum in a listProlog,在列表中找到最小值
【发布时间】:2011-04-27 06:36:23
【问题描述】:

简而言之:如何在列表中找到最小值? (感谢kaarel的建议)

长篇大论:

我在 amzi prolog 中创建了一个加权图并给定 2 个节点,我能够检索路径列表。但是,我需要在此路径中找到最小值,但无法遍历列表来执行此操作。我可以就如何确定列表中的最小值征求您的意见吗?

我的代码目前如下所示:

弧(1,2)。 弧(2,3)。 弧(3,4)。 弧(3,5)。 弧(3,6)。 弧(2,5)。 弧(5,6)。 弧(2,6)。 路径(X,Z,A):- (arc(X,Y),path(Y,Z,A1),A 为 A1+1;arc(X,Z),A 为 1)。

因此,'键控 findall(Z,path(2,6,Z),L).' in listener 允许我获得一个列表 [3,2,2,1]。 我需要从这里检索最小值并将其乘以一个数量。有人可以建议如何检索最小值吗?谢谢!

【问题讨论】:

  • 请用一个句子“如何确定列表中的最小数字?”替换您的问题文本;)

标签: list prolog minimum traversal


【解决方案1】:

这对我来说是正确的 (from here)。

min_in_list([Min],Min).                 % We've found the minimum

min_in_list([H,K|T],M) :-
    H =< K,                             % H is less than or equal to K
    min_in_list([H|T],M).               % so use H

min_in_list([H,K|T],M) :-
    H > K,                              % H is greater than K
    min_in_list([K|T],M).               % so use K

【讨论】:

  • 谢谢安德索。在我在这里提交我的 qn 之前,我实际上是在网上搜索时看到了这段代码。不知怎的,我不明白,现在我明白了,谢谢!
  • 自从我在 Prolog 中思考以来已经有好几年了,但我怀疑 @mat 对我发布的内容具有可读性优势......
  • 您的解决方案的问题还在于您将 H 和 K 比较了两次,min-function 可能不会这样做...
【解决方案2】:

SWI-Prolog 有 min_list/2:

min_list(+List, -Min)
    True if Min is the smallest number in List.

它的定义在library/lists.pl

min_list([H|T], Min) :-
    min_list(T, H, Min).

min_list([], Min, Min).
min_list([H|T], Min0, Min) :-
    Min1 is min(H, Min0),
    min_list(T, Min1, Min).

【讨论】:

    【解决方案3】:

    通常使用所谓的“滞后参数”从第一个参数索引中受益:

    list_min([L|Ls], Min) :-
        list_min(Ls, L, Min).
    
    list_min([], Min, Min).
    list_min([L|Ls], Min0, Min) :-
        Min1 is min(L, Min0),
        list_min(Ls, Min1, Min).
    

    这种模式称为折叠(从左至右),而foldl/4(在最近的 SWI 版本中可用)允许您将其写为:

    list_min([L|Ls], Min) :- foldl(num_num_min, Ls, L, Min).
    
    num_num_min(X, Y, Min) :- Min is min(X, Y).
    


    请注意,这不能用于所有方向,例如:

    ?- list_min([A,B], 5).
    is/2: Arguments are not sufficiently instantiated
    

    如果您正在推理 整数,就像您的示例中的情况一样,因此我建议您使用 CLP(FD) 约束来自然地概括谓词。而不是(is)/2,只需使用(#=)/2 并从更具声明性的解决方案中受益:

    :- use_module(library(clpfd)).
    
    list_min([L|Ls], Min) :- foldl(num_num_min, Ls, L, Min).
    
    num_num_min(X, Y, Min) :- Min #= min(X, Y).
    

    这可以用作在各个方向都有效的真实关系,例如:

    ?- list_min([A,B], 5).
    

    屈服:

    A in 5..sup,
    5#=min(B, A),
    B in 5..sup.
    

    【讨论】:

      【解决方案4】:

      感谢您的回复。很有用。我还进行了进一步的实验并开发了这个答案:

      % if list has only 1 element, it is the smallest. also, this is base case.
      min_list([X],X).
      
      min_list([H|List],X) :-
      min_list(List,X1), (H =< X1,X is H; H > X1, X is X1).
      
      % recursively call min_list with list and value,
      % if H is less than X1, X1 is H, else it is the same. 
      

      尚不确定如何从算法上衡量答案的好坏,但它确实有效!尽管如此,将不胜感激任何反馈。谢谢!

      【讨论】:

        【解决方案5】:
        %Usage: minl(List, Minimum).
        minl([Only], Only).
        minl([Head|Tail], Minimum) :-
            minl(Tail, TailMin),
            Minimum is min(Head, TailMin). 
        

        第二条规则进行递归,用英文“获取尾部的最小值,并将最小值设置为该值和头部中的较小值”。第一条规则是基本情况,“一个列表的最小值,是列表中唯一的值”。

        测试:

        | ?- minl([2,4,1],1).
        
        true ? 
        
        yes
        | ?- minl([2,4,1],X).
        
        X = 1 ? 
        
        yes
        

        您可以使用它来检查第一种情况下的值,或者您可以让 prolog 计算第二种情况下的值。

        【讨论】:

          【解决方案6】:
          min([Second_Last, Last], Result):-
              Second_Last < Last
           -> Result = Second_Last
           ;  Result = Last, !.
          
          min([First, Second|Rest], Result):-
              First < Second
           -> min([First|Rest], Result)
           ;  min([Second|Rest], Result).
          

          应该可以工作了。

          【讨论】:

            【解决方案7】:

            SWI-Prolog 提供库(聚合)。通用性和性能方面。

            :- [library(aggregate)].
            min(L, M) :- aggregate(min(E), member(E, L), M).
            

            编辑

            最近添加的是库 (solution_sequences)。现在我们可以写了

            min(L,M) :- order_by([asc(M)], member(M,L)), !.
            max(L,M) :- order_by([desc(M)], member(M,L)), !.
            

            现在,准备好惊喜 :) ?

            ?- test_performance([clpfd_max,slow_max,member_max,rel_max,agg_max]).
            clpfd_max:99999996
            % 1,500,000 inferences, 0.607 CPU in 0.607 seconds (100% CPU, 2470519 Lips)
            slow_max:99999996
            % 9,500,376 inferences, 2.564 CPU in 2.564 seconds (100% CPU, 3705655 Lips)
            member_max:99999996
            % 1,500,009 inferences, 1.004 CPU in 1.004 seconds (100% CPU, 1494329 Lips)
            rel_max:99999996
            % 1,000,054 inferences, 2.649 CPU in 2.648 seconds (100% CPU, 377588 Lips)
            agg_max:99999996
            % 2,500,028 inferences, 1.461 CPU in 1.462 seconds (100% CPU, 1710732 Lips)
            true 
            
            with these definitions:
            
            ```erlang
            :- use_module(library(clpfd)).
            
            clpfd_max([L|Ls], Max) :- foldl([X,Y,M]>>(M #= max(X, Y)), Ls, L, Max).
            
            slow_max(L, Max) :-
               select(Max, L, Rest), \+ (member(E, Rest), E @> Max).
            
            member_max([H|T],M) :-
                member_max(T,N), ( \+ H@<N -> M=H ; M=N ).
            member_max([M],M).
            
            rel_max(L,M) :-
                order_by([desc(M)], member(M,L)), !.
            
            agg_max(L,M) :-
                aggregate(max(E), member(E,L), M).
            
            test_performance(Ps) :-
                test_performance(Ps,500 000,_).
            test_performance(Ps,N_Ints,Result) :-
                list_of_random(N_Ints,1,100 000 000,Seq),
                maplist({Seq}/[P,N]>>time((call(P,Seq,N),write(P:N))),Ps,Ns),
                assertion(sort(Ns,[Result])).
            
            list_of_random(N_Ints,L,U,RandomInts) :-
                length(RandomInts,N_Ints),
                maplist({L,U}/[Int]>>random_between(L,U,Int),RandomInts).
            
            

            clpfd_max 胜出,令我惊讶的是,slow_max/2 结果还不错......

            【讨论】:

              【解决方案8】:

              与 andersoj 类似,但使用剪切而不是双重比较:

              min([X], X).
              
              min([X, Y | R], Min) :-
                  X < Y, !,
                  min([X | R], Min).
              
              min([X, Y | R], Min) :-
                 min([Y | R], Min).
              

              【讨论】:

                【解决方案9】:

                没有“是”的解决方案。

                min([],X,X).
                min([H|T],M,X) :- H =< M, min(T,H,X).
                min([H|T],M,X) :- M < H, min(T,M,X).
                min([H|T],X) :- min(T,H,X).
                

                【讨论】:

                • min([1,1],X). 应该成功,但它失败了。
                【解决方案10】:

                % 在列表中找到最小值

                min([Y],Y):-!.
                
                min([H|L],H):-min(L,Z),H=<Z.
                
                min([H|L],Z):-min(L,Z),H>=Z.
                

                % 所以你怎么想!

                【讨论】:

                • 乍一看可能看起来很优雅 - 然而,不幸的是,它不是确定性的,甚至可以产生比列表中的元素更多的解决方案。例如:?- min([1,1,1,1], Min).,它产生 8 个解决方案。或者在 SWI-Prolog 中尝试?- numlist(1, 1000, Ls), min(Ls, Min).,并在它发出Min = 1 后按空格键以获得进一步的解决方案,并准备等待。此外,它不是尾递归的,因此不太适用于长列表:例如 ?- numlist(1, 10_000_000, Ls), min(Ls, Min).,它会产生本地堆栈溢出,而此处发布的其他版本则不会。
                【解决方案11】:

                这很有效,而且看起来相当有效。

                min_in_list([M],M).    
                min_in_list([H|T],X) :-
                    min_in_list(T,M),
                    (H < M, X = H; X = M).   
                
                min_list(X,Y) :- min_in_list(X,Y), !.
                

                【讨论】:

                • prolog-cut 使您的代码失去了稳定性,这意味着代码在“恰到好处的实例化数量”的情况下在逻辑上表现良好,但在使用时会逻辑上不健全 太多实例化的术语。这就是?- min_in_list([1,2,3],2). 成功的原因——它应该失败!
                【解决方案12】:

                这个程序可能很慢,但我喜欢尽可能写出明显正确的代码。

                最小(列表,最小值):- 排序(列表,[Min|_])。

                【讨论】:

                • 是的,但是包含逻辑变量的输入列表呢?
                【解决方案13】:

                这对我来说没问题:

                minimumList([X], X).        %(The minimum is the only element in the list)
                
                minimumList([X|Q], M) :-    % We 'cut' our list to have one element, and the rest in Q
                 minimumList(Q, M1),         % We call our predicate again with the smallest list Q, the minimum will be in M1
                 M is min(M1, X).            % We check if our first element X is smaller than M1 as we unstack our calls
                
                

                【讨论】:

                  猜你喜欢
                  • 2017-09-19
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-11-16
                  • 2015-05-03
                  • 2013-04-27
                  • 2018-03-11
                  相关资源
                  最近更新 更多