微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在 Picat 中构建格雷码生成器?

如何解决如何在 Picat 中构建格雷码生成器?

受到我从上一篇文章的回答中获得的知识的鼓舞,我的目标是生成给定长度的格雷码。过程 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],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],H) ?=> 
    H1 #!= H2,A1 #= A + 1,hamming(T1,T2,A1,H).
hamming([H1|T1],H) ?=> 
    H1 #= H2,A1 #= A + 0,H).

解决方法

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

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

% ....

gray(CodeLen) =>
    CodeNr is 2**CodeLen,Codes = new_array(CodeNr,CodeLen).array_matrix_to_list_matrix,% <--
    Codes :: 0..1,% ....
  1. 使用hamming/4 函数将调用中的数组参数转换为to_list() 列表。例如:
    % ...
    foreach(CodeNr1 in 1..CodeNr)
        CodeNr2 = cond(CodeNr1 == CodeNr,1,CodeNr1 + 1),% hamming(Codes[CodeNr1],Codes[CodeNr2],H),% Original
        hamming(Codes[CodeNr1].to_list,Codes[CodeNr2].to_list,% <---        
        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 :: 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,hamming_distance(Codes[CodeNr1],1),end,% around the corner
    % hamming_distance(Codes[1],Codes[CodeNr],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],[1,1],[0,0]]
codesNum = [0,8,12,14,15,13,9,11,6,7,3,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).

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。