【问题标题】:How to build a Gray-code generator in Picat?如何在 Picat 中构建格雷码生成器?
【发布时间】:2021-04-19 13:46:15
【问题描述】:

受到我从上一篇文章的答案中获得的知识的鼓舞,我的目标是生成给定长度的格雷码。程序hamming 似乎工作正常,但是,Picat 系统找不到解决方案。哪里错了?

import cp.
main => gray(2).

gray(CodeLen) =>
    CodeNr is 2**CodeLen,
    Codes = new_array(CodeNr, CodeLen),
    Codes :: 0..1,

    foreach(CodeNr1 in 1..CodeNr)
        CodeNr2 = cond(CodeNr1 == CodeNr, 1, CodeNr1 + 1),
        hamming(Codes[CodeNr1], Codes[CodeNr2], 0, H), 
        H #= 1 
        % the Hamming distance between 2 consecutive codes is 1
    end,

    solve(Codes),
    printf("%w\n", Codes).

hamming([], [], A, H) ?=> H #= A.
hamming([H1|T1], [H2|T2], A, H) ?=> 
    H1 #!= H2,
    A1 #= A + 1,
    hamming(T1, T2, A1, H).
hamming([H1|T1], [H2|T2], A, H) ?=> 
    H1 #= H2,
    A1 #= A + 0,
    hamming(T1, T2, A1, H).

【问题讨论】:

  • 似乎工作正常,但没有解决方案”似乎是一个非常不清楚或自相矛盾的问题描述。你的意思是?应该发生什么,又会发生什么?
  • 没有解决方案意味着 Picat 系统会回答 no,而不是打印格雷码表。

标签: picat


【解决方案1】:

模型不打印任何内容的原因是您在数组矩阵 Code 上使用了列表构造 ([H|T]),这是不允许的。您必须将矩阵的行(它们是数组)转换为列表。这可以通过两种方式完成:

  1. array_matrix_to_list_matrix()将数组矩阵Code矩阵转换为列表矩阵(需要加载util包):
import util.

% ....

gray(CodeLen) =>
    CodeNr is 2**CodeLen,
    Codes = new_array(CodeNr, CodeLen).array_matrix_to_list_matrix, % <--
    Codes :: 0..1,
    % ....
  1. 使用to_list() 函数将调用hamming/4 中的数组参数转换为列表。例如:
    % ...
    foreach(CodeNr1 in 1..CodeNr)
        CodeNr2 = cond(CodeNr1 == CodeNr, 1, CodeNr1 + 1),
        % hamming(Codes[CodeNr1], Codes[CodeNr2], 0, H), % Original
        hamming(Codes[CodeNr1].to_list, Codes[CodeNr2].to_list, 0, H), % <---        
        H #= 1 
        % the Hamming distance between 2 consecutive codes is 1
    end,
    % ...

更新

这是一个约束模型,它解决了生成注释中指示的不同行的问题。它使用hamming_distance 的更简单版本,只需用sum 计算不同位的数量。 另外,为了对称,我要求第一行和最后一行的汉明距离也为 1。(这是在原始代码中。)

为了要求不同的行,约束to_num/3 用于将数字转换为数组中的数字(给定一个基数,此处为 2)。这些数字(必须是不同的)在CodesNum 列表中。

import cp,util.
main =>
   go.

go ?=>
  gray(5),
  nl,
  % fail,
  nl.
go => true.

% First solution for N=2..10
go2 ?=>
  foreach(N in 2..10)
    println(n=N),
    if time(gray(N)) then
      true
    else
      println(nope)
    end,
    nl
  end,
  nl.
go2 => true.


gray(CodeLen) =>
    CodeNr is 2**CodeLen,
    println(codeNr=CodeNr),
    Codes = new_array(CodeNr, CodeLen).array_matrix_to_list_matrix,
    Codes :: 0..1,
    CodesNum = new_list(CodeNr), % array -> integer
    CodesNum :: 0..CodeNr,

    
    foreach(CodeNr1 in 1..CodeNr)
        to_num(Codes[CodeNr1],2,CodesNum[CodeNr1]),
        CodeNr2 = cond(CodeNr1 == CodeNr, 1, CodeNr1 + 1),
        hamming_distance(Codes[CodeNr1], Codes[CodeNr2], 1),
    end,
    % around the corner
    % hamming_distance(Codes[1], Codes[CodeNr],1),
            
    all_different(CodesNum),
    CodesNum[1] #= 0, % symmetry breaking
    Vars = CodesNum ++ Codes.vars,
    solve($[ff,updown],Vars),
    printf("%w\n", Codes),
    println(codesNum=CodesNum),nl.

% Hamming distance of As and Bs
hamming_distance(As, Bs,Diff) =>
   Diff #= sum([(A #!= B) : {A,B} in zip(As,Bs)]).

% Convert Num to/from a list of digits in List (base Base)
to_num(List, Base, Num) =>
        Len = length(List),
        Num #= sum([List[I]*Base**(Len-I) : I in 1..Len]).

to_num(List, Num) =>
       to_num(List, 10, Num).

它在 0 秒内解决了 N=4:

n = 4
codeNr = 16
[[0,0,0,0],[1,0,0,0],[1,1,0,0],[1,1,1,0],[1,1,1,1],[1,1,0,1],[1,0,0,1],[1,0,1,1],[1,0,1,0],[0,0,1,0],[0,1,1,0],[0,1,1,1],[0,0,1,1],[0,0,0,1],[0,1,0,1],[0,1,0,0]]
codesNum = [0,8,12,14,15,13,9,11,10,2,6,7,3,1,5,4]

CPU time 0.0 seconds.

该模型很快解决了 N=2..7(第一个解决方案),但它在 N=8 时遇到了困难,而且我没有时间测试不同的搜索启发式以使其更快。

这是解决格雷码的另一种方法,但没有约束建模,而且速度更快:http://hakank.org/picat/gray_code.pi

Update2 这是hamming/4 的更快版本。它使用具体化(布尔)变量B 来检查H1H2 是否不同,然后可以用作添加到A0 的值。

hamming2([], [], A, A).
hamming2([H1|T1], [H2|T2], A0, H) :-
    B :: 0..1,
    H1 #!= H2 #<=> B #= 1,
    A1 #= A0 + B,
    hamming2(T1, T2, A1, H).

【讨论】:

  • 当然是数组和列表。感谢您的热心帮助。
  • 一些反馈:要生成真正的格雷码,必须应用 all_different(Codes) 约束,这会大大减慢执行速度,甚至 CodeLen=4 似乎也无法完成。
  • 抱歉,我不确定您对 all_different/1 部分的意思。你能在你的代码中展示这个吗(或者可能在一个新问题上)。
  • 啊,现在我明白你的意思了。我会用解决这个问题的方法来更新我的答案。
  • 感谢您提供的所有信息补充,请不要为速度改进而烦恼。目前的解决方案非常适合我的学习目的。
猜你喜欢
  • 2018-09-16
  • 2015-08-11
  • 2021-04-28
  • 1970-01-01
  • 1970-01-01
  • 2018-05-27
  • 2018-12-18
  • 2012-05-08
  • 1970-01-01
相关资源
最近更新 更多