Prolog - 获得用坐标绘制的三角形的最大周长

Prolog - get the greatest perimeter of triangles drawn with coordinates

平面中点的名称和坐标可以用点(A,X,Y)给出。

Prolog语言中按照这个关系定义10个点[比如point(a, 13, 47)]创建数据库

由于其中三个点将定义一个三角形,因此创建列出这些三角形的规则。

创建一条规则,通过识别三角形中周长最大的三角形在屏幕上打印。

您已经打印出程序列表,其中包含关系和规则,三角形列表,以及最大周长的三角形。

nokta(a, 3,1).
nokta(b, 6,2).
nokta(c, 7,1).
nokta(d, 9,1).
nokta(e, 6,3).
nokta(f, 4,3).
nokta(g, 9,3).
nokta(h, 13, 47).
nokta(i, 15, 49).  

istriangle(A, B, C) :-
  A > 0 ,
  B > 0 ,
  C > 0 ,
  A + B >= C ,
  A + C >= B ,
  B + C >= A .

calculate_the_perimeter_triangle(P1,P2,P3,AB,AC,BC,S) :-
  nokta(P1, X1, Y1),
  nokta(P2, X2, Y2),
  nokta(P3, X3, Y3),
  dif(P1, P2),
  dif(P2, P3),
  dif(P1, P3),
  \+ (X1 == X2, X2 == X3, X1 == X3),
  \+ (Y1 == Y2, Y2 == Y3, Y1 == Y3), 
  %IABI = √((x2 – x1) ²  + (y2 – y1) ² ) 
  AB is sqrt((X2-X1)^2 + (Y2-Y1)^2),
  %IACI = √((x3 – x1) ²  + (y3 – y1) ² ) 
  AC is sqrt((X3-X1)^2 + (Y3-Y1)^2),
  %IBCI = √((x3 – x2) ²  + (y3 – y2) ² )
  BC is sqrt((X3-X2)^2 + (Y3-Y2)^2),
  S is AB + AC + BC,
  AB > 0 ,
  AC > 0 ,
  BC > 0 .

calculate_perimeter_triangle(N1,N2,N3) :- 
  calculate_the_perimeter_triangle(X,Y,Z,N1,N2,N3,S),
  istriangle(N1,N2,N3),
  write(S - ' Bu koordinatlardan oluşan üçgen.'),
  nl,
  fail.

我得到了三角形的周长但是我无法得到三角形的周长

,nl,fail.

这导致 calculate_perimeter_triangle 失败,Prolog 回溯并重试。回溯之后,就好像谓词中的代码根本就没有 运行 一样,变量未绑定,状态丢失。只有使用 write() 会留下副作用(屏幕上的文本),您才能看到它的发生,但无法捕获该信息并将其保存在程序中。

你可以使用 findall/3

findall([nokta(ID1, X1, Y1), nokta(ID2, X2, Y2), nokta(ID3, X3, Y3)],
          (
             nokta(ID1, X1, Y1), nokta(ID2, X2, Y2), nokta(ID3, X3, Y3),
             dif(ID1, ID2), dif(ID2, ID3), dif(ID3, ID1)
          ),
        Triangles)

构建一个列表三角形,其中包含具有 3x nokta(ID, X, Y) 和不同 ID 的所有列表,然后使用这些三角形计算周长。

我可能会这样表示点,用一个方便的谓词给我一个更简洁的符号,p( Name, X:Y )

point( p(N,P) ) :- point(N,P). 

point( a ,   3 :  1 ).
point( b ,   6 :  2 ).
point( c ,   7 :  1 ).
point( d ,   9 :  1 ).
point( e ,   6 :  3 ).
point( f ,   4 :  3 ).
point( g ,   9 :  3 ).
point( h ,  13 : 47 ).
point( i ,  15 : 49 ).

然后可以通过简单地从数据库中绘制 3 个不同的点来构造一个三角形。我们可以通过

  • 获得每一分
  • 将所有 3 个组合成一个列表
  • 使用 sort/2 对列表进行排序。

sort/2 通过删除重复元素确保列表代表 set,这确保我们不会创建三角形 AAB。那只是一个线段。

sort/2 同样,由于我们将点的名称作为输出 p/2 结构中的第一个参数,因此按规范顺序对点进行排序,因此三角形 ABC 将被视为与三角形 CBA 相同。

然后,我们使用built-in atom_chars/2谓词,将三个点名称组合成一个原子来唯一标识三角形,并计算其周长。

最后我们把它组装成一个t/5这种形式的结构:

t( Name, Point1, Point2, Point3, perimeter(Perimeter) )

代码如下所示:

triangle( t(N,Pa,Pb,Pc,perimeter(P)) ) :-
  point(P1) ,
  point(P2) ,
  point(P3) ,
  sort([P1,P2,P3],[p(A,Pa),p(B,Pb),p(C,Pc)]) , 
  atom_chars(N,[A,B,C]),
  perimeter(Pa,Pb,Pc,P) .

计算周长很简单:我们只需计算三角形每条边的长度并将它们相加即可:

perimeter( P1, P2, P3, P ) :-
  distance(P1,P2,L1),
  distance(P2,P3,L2),
  distance(P3,P1,L3),
  P is L1+L2+L3
  .

并且计算线段的长度很容易:它只是笛卡尔平面上两点之间的距离,使用从勾股定理导出的距离公式:

distance( X1:Y1, X2:Y2, D ) :- D is sqrt( (X2 - X1)^2 + (Y2 - Y1)^2 ).

现在,每个三角形都带有所需的所有信息。我们可以使用 built-in set_of/3 谓词收集可以从数据库中的点集构造的三角形集:它既排序又 de-duplicates 要排序的列表:

triangles( Ts ) :- setof( T , triangle(T), Ts ).

剩下的唯一任务就是找出周长最长的三角形。这只是递归遍历列表的问题,使用 辅助谓词 将在我们进行时跟踪具有最长周长的三角形:

max_perimeter( [T|Ts] , Tmax ) :-
  max_perimeter( Ts, T, Tmax ).

max_perimeter( []     , Tmax , Tmax ).
max_perimeter( [T|Ts] , T0   , Tmax ) :-
  maxp(T,T0,T1),
  max_perimeter(Ts,T1,Tmax).

maxp( T1, T2, T1 ) :- perimeter(T1,P1), perimeter(T2,P2), P1 >= P2, !.
maxp( T1, T2, T2 ) :- perimeter(T1,P1), perimeter(T2,P2), P1 <  P2   .

perimeter( t(_,_,_,_,perimeter(P)) , P ).

然后我们可以将它们与一个 main/0 谓词放在一起来驱动它:

point( p(N,P) ) :- point(N,P).

point( a ,  3: 1  ).
point( b ,  6: 2 ).
point( c ,  7: 1 ).
point( d ,  9: 1 ).
point( e ,  6: 3 ).
point( f ,  4: 3 ).
point( g ,  9: 3 ).
point( h , 13:47 ).
point( i , 15:49 ).

main :-
  triangles(Ts)      ,
  list_triangles(Ts) ,
  max_perimeter(Ts, Tmax),
  nl(),
  writeln(longest_perimeter) ,
  writeln(Tmax).

triangles( Ts ) :- setof( T , triangle(T), Ts ) .

triangle( t(N,Pa,Pb,Pc,perimeter(P)) ) :-
  point(P1) ,
  point(P2) ,
  point(P3) ,
  sort([P1,P2,P3],[p(A,Pa),p(B,Pb),p(C,Pc)]) , 
  atom_chars(N,[A,B,C]) ,
  perimeter(Pa,Pb,Pc,P)
  .
    
perimeter( P1, P2, P3, P ) :-
  distance(P1,P2,L1),
  distance(P2,P3,L2),
  distance(P3,P1,L3),
  P is L1+L2+L3
  .

distance( X1:Y1, X2:Y2, D ) :- D is sqrt( (X2 - X1)^2 + (Y2 - Y1)^2 ) 
 .

list_triangles( [T|Ts] ) :-
   writeln(T),
   list_triangles(Ts).
list_triangles( []     ).

max_perimeter( [T|Ts] , Tmax ) :-
    max_perimeter( Ts, T, Tmax ).

max_perimeter( []     , Tmax , Tmax ).
max_perimeter( [T|Ts] , T0   , Tmax ) :-
    maxp(T,T0,T1),
    max_perimeter(Ts,T1,Tmax).

maxp( T1, T2, T1 ) :- perimeter(T1,P1), perimeter(T2,P2), P1 >= P2, ! .
maxp( T1, T2, T2 ) :- perimeter(T1,P1), perimeter(T2,P2), P1 <  P2   
 .

perimeterf( t(_,_,_,_,perimeter(P)) , P ).

由于您有一个包含 9 个点的数据集,因此有 9 * 8 * 7 个或 504 个可能的三角形。 运行这表明周长最长的三角形是

  • 三角形:ADI
  • 周长:103.85081399720322