在Prolog中旋转矩阵

时间:2016-02-24 05:33:34

标签: list matrix prolog

如何在Prolog中围绕其中心点旋转4 x 4矩阵?我可以简单地在4 x 4矩阵的情况下重新排列元素,但是对于像N x N这样的一般情况,如何做到这一点?

3 个答案:

答案 0 :(得分:3)

你想要的是不完全矩阵换位......但差不多!

:- use_module(library(clpfd)).

matrix_rotated(Xss, Zss) :-
   transpose(Xss, Yss),
   maplist(reverse, Yss, Zss).

示例查询:

?- matrix_rotated([[a1,a2,a3,a4],
                   [b1,b2,b3,b4],
                   [c1,c2,c3,c4]], Xss).
Xss = [[c1,b1,a1],
       [c2,b2,a2],
       [c3,b3,a3],
       [c4,b4,a4]].

答案 1 :(得分:3)

有人是wrong on the internet,所以值班。 毕竟,我们必须遵守Cunningham's Law

此答案充分利用了library(clpfd)中的SWI-Prolog

:- use_module(library(clpfd)).

rotate_clockwise(Matrix, N, Rotated) :-
    N_mod_4 #= N mod 4,
    rotate_clockwise_(N_mod_4, Matrix, Rotated).

rotate_clockwise_(0, M, M).
rotate_clockwise_(1, M, R) :-
    transpose(M, R0),
    maplist(reverse, R0, R).
rotate_clockwise_(2, M, R) :-
    reverse(M, R0),
    maplist(reverse, R0, R).
rotate_clockwise_(3, M, R) :-
    transpose(M, R0),
    reverse(R0, R).

您可以通过将矩形矩阵翻转两次来旋转它(用书或其他东西试试)。 根据您翻转的两个轴,您可以获得3个非平凡旋转中的一个:如果我们同意"一次旋转"顺时针旋转90度,然后你可以旋转0,1,2或3次。在上面的代码中:

  • 沿水平轴翻转reverse
  • 沿垂直轴翻转是maplist(reverse)
  • 沿着左上 - 右下轴翻转transpose

至于为什么不定义旋转90度顺时针旋转然后旋转多次:好吧,它实际上是更多的代码! 根据上面的定义,无论我们给rotate_clockwise/3提供什么有效参数,它都会进行正确的轮换,不进行不必要的翻转,并使调用者代码保持简短。

?- rotate_clockwise([[a,b,c],[d,e,f]], -1, R). % once counter-clockwise
R = [[c, f], [b, e], [a, d]].

?- rotate_clockwise([[a,b,c],[d,e,f]], 2, R). % 180 degrees
R = [[f, e, d], [c, b, a]].

?- rotate_clockwise([[a,b,c],[d,e,f]], 168, R). % rotate once each hour, for a week
R = [[a, b, c], [d, e, f]].

?- rotate_clockwise([[a,b,c],[d,e,f]], N, R). % enumerate all possibilities
R = [[a, b, c], [d, e, f]],
N mod 4#=0 ;
R = [[d, a], [e, b], [f, c]],
N mod 4#=1 ;
R = [[f, e, d], [c, b, a]],
N mod 4#=2 ;
R = [[c, f], [b, e], [a, d]],
N mod 4#=3.

?- Matrix = [[a],[b]],
   rotate_clockwise(Matrix, -1, R),
   rotate_clockwise(Matrix, 3, R). % those two mean the same
Matrix = [[a], [b]],
R = [[a, b]].

答案 2 :(得分:1)

如上所述,transpose / 2不是正确的解决方案。我编写了一些东西(可能有点太一般),允许指定转移到右边的单元格数量的旋转量。 Shift默认为1,但后来我注意到为了获得90°旋转,换档在传递到内框时减小。将匿名变量传递给matrix_rotate / 3具有使用当前内部帧大小的效果。然后,它顺时针旋转90°。

/** <module> matrix_rotate
 *
 *  answer to http://stackoverflow.com/questions/35594027/rotate-a-matrix-in-prolog
 *  --------
 *
 *  source file /home/carlo/prolog/matrix_rotate.pl
 *  created at mer feb 24 16:43:50 2016
 *
 *  @author carlo
 *  @version 0.9.9
 *  @copyright carlo
 *  @license LGPL v2.1
 */

:- module(matrix_rotate,
    [matrix_rotate/2
    ,matrix_rotate/3
    ,matrix_allocate/4
    ,matrix_row_col_element/4
    ,rowcol/3
    ]).

:- meta_predicate matrix_allocate(3, +,+,-).

matrix_allocate(Pred, NRows, NCols, Mat) :-
    bagof(Vs, Row^(
        between(1, NRows, Row),
        bagof(C, Col^(between(1, NCols, Col), call(Pred, Row, Col, C)), Vs)
    ), Mat).

empty(_R,_C,_).
rowcol(R, C, (R,C)).

matrix_rotate(Mat, Rot) :-
    matrix_rotate(Mat, 1, Rot).
matrix_rotate(Mat, Shift, Rot) :-
    matrix_is_square(Mat, Size),
    matrix_allocate(empty, Size, Size, Rot),
    frames_shift(Mat, Shift, 1, Rot),
    (   Size mod 2 =:= 1
    ->  Cen is Size // 2 + 1,
        matrix_row_col_element(Mat, Cen, Cen, E),
        matrix_row_col_element(Rot, Cen, Cen, E)
    ;   true
    ).

frames_shift(Mat, Shift, Frame, Rot) :-
    length(Mat, Size),
    Frame =< Size // 2,
    (   integer(Shift)
    ->  ActualShift = Shift
    ;   ActualShift is Size - (Frame - 1)*2 - 1
    ),
    frame(Mat, Frame, L),
    shift_right(L, ActualShift, S),
    frame(Rot, Frame, S),
    F is Frame+1,
    !, frames_shift(Mat, Shift, F, Rot).
frames_shift(_Mat, _Shift, _Frame, _Rot).

matrix_is_square(Mat, Size) :-
    length(Mat, Size),
    forall(member(Row, Mat), length(Row, Size)).

matrix_row_col_element(Mat, Row, Col, El) :-
    nth1(Row, Mat, Cells),
    nth1(Col, Cells, El).

shift_right(List, Shift, Shifted) :-
    length(Post, Shift),
    append(Pre, Post, List),
    append(Post, Pre, Shifted).

frame(Mat, N, Frame) :-
    length(Mat, S),
    T is N, B is S-N+1,
    L is N, R is S-N+1,
    matrix_range_elements(Mat, T,T, L,R-1, Top),
    matrix_range_elements(Mat, T,B-1, R,R, Right),
    matrix_range_elements(Mat, B,B, R,L+1, Bottom),
    matrix_range_elements(Mat, B,T+1, L,L, Left),
    append([Top, Right, Bottom, Left], Frame).

matrix_range_elements(Mat, RStart, RStop, CStart, CStop, Elems) :-
    bagof(E, matrix_element_between(Mat, RStart, RStop, CStart, CStop, E), Elems).

matrix_element_between(Mat, RStart, RStop, CStart, CStop, Elem) :-
    signed_between(RStart, RStop, R),
    signed_between(CStart, CStop, C),
    matrix_row_col_element(Mat, R, C, Elem).

signed_between(Start, Stop, I) :-
    A is Start, B is Stop,
    ( B < A -> between(B, A, N), I is A+B-N ; between(A, B, I) ).

此片段突出显示了将矩阵作为列表列表处理时有用的一般模式。还有一些困难......

使用示例:

?- matrix_allocate(rowcol,4,4,M),matrix_rotate(M,_,R),maplist(writeln,R).
[(4,1),(3,1),(2,1),(1,1)]
[(4,2),(3,2),(2,2),(1,2)]
[(4,3),(3,3),(2,3),(1,3)]
[(4,4),(3,4),(2,4),(1,4)]
M = [[(1, 1),  (1, 2),  (1, 3),  (1, 4)], [(2, 1),  (2, 2),  (2, 3),  (2, 4)], [(3, 1),  (3, 2),  (3, 3),  (3, 4)], [(4, 1),  (4, 2),  (4, 3),  (4, 4)]],
R = [[(4, 1),  (3, 1),  (2, 1),  (1, 1)], [(4, 2),  (3, 2),  (2, 2),  (1, 2)], [(4, 3),  (3, 3),  (2, 3),  (1, 3)], [(4, 4),  (3, 4),  (2, 4),  (1, 4)]].

?- matrix_allocate(rowcol,3,3,M),matrix_rotate(M,_,R),maplist(writeln,R).
[(3,1),(2,1),(1,1)]
[(3,2),(2,2),(1,2)]
[(3,3),(2,3),(1,3)]
M = [[(1, 1),  (1, 2),  (1, 3)], [(2, 1),  (2, 2),  (2, 3)], [(3, 1),  (3, 2),  (3, 3)]],
R = [[(3, 1),  (2, 1),  (1, 1)], [(3, 2),  (2, 2),  (1, 2)], [(3, 3),  (2, 3),  (1, 3)]].