如何在 Picat 中构建格雷码生成器?
How to build a Gray-code generator in Picat?
我从我以前 post 的答案中获得的知识鼓舞了我,我的目标是生成给定长度的格雷码。程序 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).
模型不打印任何内容的原因是您在数组矩阵 Code
上使用列表结构 ([H|T]
),这是不允许的。您必须将矩阵的行(数组)转换为列表。这可以通过两种方式完成:
- 用
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,
% ....
- 使用
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).
它在0s中解决了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
来检查 H1
和 H2
是否不同,然后可以用作添加到 A0
.[=32= 的值]
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).
我从我以前 post 的答案中获得的知识鼓舞了我,我的目标是生成给定长度的格雷码。程序 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).
模型不打印任何内容的原因是您在数组矩阵 Code
上使用列表结构 ([H|T]
),这是不允许的。您必须将矩阵的行(数组)转换为列表。这可以通过两种方式完成:
- 用
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,
% ....
- 使用
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).
它在0s中解决了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
来检查 H1
和 H2
是否不同,然后可以用作添加到 A0
.[=32= 的值]
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).