【问题标题】:How to - Graph Coloring in Prolog如何 - Prolog 中的图形着色
【发布时间】:2024-01-10 21:26:02
【问题描述】:

我正在尝试在 Prolog 中制作简单的图形着色算法,但我有点难以理解该语言。我知道我想做什么 - 我想去一个顶点,找到所有其他连接到它的顶点,检查我的顶点的颜色,并根据它用不同的颜色为其他顶点着色。我只是很难将其翻译成 Prolog。如果它是 C 方言或 Java,那对我来说简直是小菜一碟,但这让我很适应。

这是我目前所拥有的:

main:- graph_coloring.

%color_list([blue, red, green, yellow, white], colors).
%vertex_list([a, b, c, d], vertices).
%edge_list([(a,b),(b,c),(c,d)], edges).

%Our graph
color(blue).
color(red).
color(green).
color(black).
color(white).

%graph([a-b, b-c, b-d, c-d]).

vertex(a).
vertex(b).
vertex(c).
vertex(d).

%Subject to changing, so are asserted into listener at runtime.
init_dynamic_facts:-
    assertz(vertex_color(a, none)),
    assertz(vertex_color(b, none)),
    assertz(vertex_color(c, none)),
    assertz(vertex_color(d, none)),
    assertz(location(a)).

edge(a,b).
edge(b,c).
edge(b,d).
edge(c,d).

is_connect(A,B):-
    edge(A,B).
is_connect(A,B):-
    edge(B,A).

connections(Vertex):-
    edge(Vertex,X).
connections(Vertex):-
    edge(X,Vertex).

move(Vertex):-
    retract(location(_)),
    asserta(location(Vertex)).

paint_vertex(Vertex, Color):-
    retract(vertex_color(Vertex,_)),
    asserta(vertex_color(Vertex, Color)).

find_vertex_color(Vertex):-
    vertex_color(Vertex, X).


graph_coloring:-

    location(Current_vertex),
    vertex_color(Current_vertex, Curr_color),
    ( Curr_color =:= none ->
        connections(Current_vertex, Others),
        vertex_color(Others, Other_colors),
        paint_vertex(Current_vertex, 

如何完成这个算法?

(已编辑:graph_coloring 下有更多代码)

【问题讨论】:

  • 你不想去一个未着色的顶点,然后选择一个没有被任何相邻顶点使用的颜色吗?
  • 我从没有着色的顶点开始,但可以肯定的是,最好为第一个顶点分配颜色并按照您所说的去做。
  • 您能具体说明您遇到的问题吗?
  • 我正在尝试在 prolog 中进行图形着色算法,但它与我使用过的其他任何东西都如此不同,我什至不确定从哪里开始。例如,变量。它们在序言中的使用方式不同,我发现它们的要求甚至不同。例如,在 C# 中,我可以说 Variable_I_want = Method_that_returns_the_variable()。我不能在 prolog 中完全做到这一点,而且我还没有弄清楚如何以“prolog 方式”做到这一点。
  • 也许你应该从更简单的开始,比如为你的所有顶点分配一种特定的颜色。

标签: algorithm prolog graph-theory graph-coloring


【解决方案1】:

我想提一下这个问题是一个典型的约束满足问题,可以使用SWI-Prolog的CSP模块来解决效率问题。这是完整的算法:

:- use_module(library(clpfd)). 

color(red).
color(blue).
color(green).

vertex(a).
vertex(b).
vertex(c).
vertex(d).
vertex(e).

edge(a,b).
edge(a,c).
edge(b,c).
edge(b,d).
edge(c,d).


colorGraph(ColorList) :- 
  findall((X, Y), edge(X, Y), Edges),
  findall(X, vertex(X), Vertexes),
  findall(hasColor(X, _), member(X, Vertexes), ColorList),
  createConstraint(Edges,ColorList),
  colorNodes(ColorList).

createConstraint([],_).
createConstraint([(V1,V2)|RL],ColorList):-
  member(hasColor(V1,C1),ColorList),
  member(hasColor(V2,C2),ColorList),
  dif(C1,C2),
  createConstraint(RL,ColorList).

colorNodes([]).
colorNodes([hasColor(_,C)|Nodes]) :-
  color(C),
  colorNodes(Nodes).

color/1 表示可用的颜色,vertex/1 表示图中的顶点,edge/2 定义顶点之间的对。此外,colorGraph(?List) 确定顶点的颜色,其中ListhasColor(Vertex, Color) 子句的列表,Vertex 是使用Color 的彩色顶点。

让我们详细说明上述算法的每个部分,以了解发生了什么。

:- use_module(library(clpfd)).

它指示SWI-Prolog加载包含约束满足问题的谓词的模块。

colorGraph(ColorList) :- 
  findall((X, Y), edge(X, Y), Edges),
  findall(X, vertex(X), Vertexes),
  findall(hasColor(X, _), member(X, Vertexes), ColorList),
  createConstraint(Edges,ColorList),
  colorNodes(ColorList).

谓词colorGraph/1 是算法的入口点。它将边/顶点的子句转换为列表,约束ColorList 以定义顶点列表,最后创建颜色约束并分配颜色(它们是两个独立的操作)。

createConstraint([],_).
createConstraint([(V1,V2)|RL],ColorList):-
  member(hasColor(V1,C1),ColorList),
  member(hasColor(V2,C2),ColorList),
  dif(C1,C2),
  createConstraint(RL,ColorList).

预测createConstraint/2 只是声明两个链接的顶点必须具有不同的颜色。值得一提的是dif/2是一个CSP谓词。

colorNodes([]).
colorNodes([hasColor(_,C)|Nodes]) :-
  color(C),
  colorNodes(Nodes).

谓词colorNodes/1 为顶点分配正确的颜色。 Prolog 会注意根据上面定义的约束分配正确的颜色。

最后可以通过调用谓词colorGraph/1找到结果,如:

?- colorGraph(L).
L = [hasColor(a, red), hasColor(b, blue), hasColor(c, green), hasColor(d, red), hasColor(e, red)] ;
L = [hasColor(a, red), hasColor(b, blue), hasColor(c, green), hasColor(d, red), hasColor(e, blue)] ;
L = [hasColor(a, red), hasColor(b, blue), hasColor(c, green), hasColor(d, red), hasColor(e, green)] ;
L = [hasColor(a, red), hasColor(b, green), hasColor(c, blue), hasColor(d, red), hasColor(e, red)] ;
L = [hasColor(a, red), hasColor(b, green), hasColor(c, blue), hasColor(d, red), hasColor(e, blue)] ;
L = [hasColor(a, red), hasColor(b, green), hasColor(c, blue), hasColor(d, red), hasColor(e, green)] ;
L = [hasColor(a, blue), hasColor(b, red), hasColor(c, green), hasColor(d, blue), hasColor(e, red)] ;
L = [hasColor(a, blue), hasColor(b, red), hasColor(c, green), hasColor(d, blue), hasColor(e, blue)] ;
L = [hasColor(a, blue), hasColor(b, red), hasColor(c, green), hasColor(d, blue), hasColor(e, green)] ;
L = [hasColor(a, blue), hasColor(b, green), hasColor(c, red), hasColor(d, blue), hasColor(e, red)] ;
L = [hasColor(a, blue), hasColor(b, green), hasColor(c, red), hasColor(d, blue), hasColor(e, blue)] ;
L = [hasColor(a, blue), hasColor(b, green), hasColor(c, red), hasColor(d, blue), hasColor(e, green)] ;
L = [hasColor(a, green), hasColor(b, red), hasColor(c, blue), hasColor(d, green), hasColor(e, red)] ;
L = [hasColor(a, green), hasColor(b, red), hasColor(c, blue), hasColor(d, green), hasColor(e, blue)] ;
L = [hasColor(a, green), hasColor(b, red), hasColor(c, blue), hasColor(d, green), hasColor(e, green)] ;
L = [hasColor(a, green), hasColor(b, blue), hasColor(c, red), hasColor(d, green), hasColor(e, red)] ;
L = [hasColor(a, green), hasColor(b, blue), hasColor(c, red), hasColor(d, green), hasColor(e, blue)] ;
L = [hasColor(a, green), hasColor(b, blue), hasColor(c, red), hasColor(d, green), hasColor(e, green)] ;

【讨论】:

    【解决方案2】:

    我认为您正在尝试以不适合 Prolog 程序的方式思考;也就是说,您试图不使用递归:) 我想出的是以下内容,但这可能并不完全正确(为时已晚,而且我在尝试思考时没有良好的声誉,例如这...:) )

    假设您拥有由以下事实描述的图表:

    edge(a,b).
    edge(b,c).
    edge(b,d).
    edge(c,d).
    

    并且可用的颜色是

    color(blue).
    color(red).
    color(green).
    

    (你只需要 3 种颜色来为平面图着色,所以我们在这里只使用 3 种颜色)。我们还假设您希望以 [Vertex-Color] 列表的形式给出答案,其中列表将包含图形每个顶点的颜色。我相信以下是正确的解决方案:

    coloring([V-C]) :-
            color(C),
            \+ edge(V,_).
    coloring([V-C,V1-C1|Coloring]) :-
            color(C),
            edge(V,V1),
            V \== V1,
            coloring([V1-C1|Coloring]),
            C1 \== C.
    

    第一个子句说如果从 V 到任何其他顶点都没有边,则尝试所有可能的颜色。第二个子句说顶点 V 将获得颜色 C,如果从 V 到 V1 有一条边,则顶点 V1 将获得颜色 C1,其中 V!= V1 和 C!= C1。 (我还假设您的图是连接的,即没有不连接到其他顶点的顶点)。

    由于我们只想要所有顶点都有颜色的解决方案,我们将只保留长度为 |V| 的列表,其中 V 是您拥有的顶点集。您可以通过多种方式实现此限制;我更喜欢使用“findall/3”:

    colors(X) :-
            coloring(X),
            findall(V,edge(V,_),List),
            length(List,Len),
            length(X,Len).
    

    现在,通过咨询该程序并询问|?- colors(X).,您将获得图表顶点的所有可能颜色分配。

    如果有人发现问题,我几乎可以肯定上述解决方案中存在,请告诉我们:)

    斯派罗斯

    【讨论】:

    • 非常感谢斯派罗斯!现在我正试图弄清楚为什么颜色(X)。正在返回“不”,但现在我对这一点有了更多的了解。应该有一个像 [a-b, b-c, b-d, c-d] 这样的列表,还是应该只使用 egde/2 和 color/1 事实运行代码?
    • 显然列表是问题所在;在 findall(...) 之后,length(List, Len) 失败,因为由于某种原因,列表是“错误目标:H38”。
    • 我在 XSB-Prolog 中运行了上面的代码,它给出了正确的(我希望 :))答案。您使用的是哪个 Prolog 编译器?也许 findall/3 在其他 Prologs 中需要不同的参数……这里使用 findall/3 只是为了找出你有多少个顶点。如果需要,您可以将其替换为实际数字。只需添加一个“vertices(4)”事实,它将包含您总共拥有的顶点数,并将对 findall 和 length(List,Len) 的调用替换为 vertices(Len)。我的源代码中唯一的东西是edge/2、color/1、coloring/1和colors/1的定义。