Prolog 如何从带有条件的字母表中生成长度为N的所有单词
从字母表{a,b,c,d,e,f}生成长度为N的所有单词,其中4个字母出现两次,所有其他字母出现一次或根本不出现 我发现了这个从字母表生成单词的代码Prolog 如何从带有条件的字母表中生成长度为N的所有单词,prolog,Prolog,从字母表{a,b,c,d,e,f}生成长度为N的所有单词,其中4个字母出现两次,所有其他字母出现一次或根本不出现 我发现了这个从字母表生成单词的代码 letter(X) :- member(X, [a, b, c, d, e, f]). word(0, []). word(N, [C|W]) :- N > 0, N1 is N-1, letter(C), word(N1, W). words(N, L) :- findall(W, word(N,
letter(X) :- member(X, [a, b, c, d, e, f]).
word(0, []).
word(N, [C|W]) :-
N > 0,
N1 is N-1,
letter(C),
word(N1, W).
words(N, L) :-
findall(W, word(N, W), Ws),
maplist(atomic_list_concat, Ws, L).
我决定把所有的信都扔了,然后挑选那些符合我条件的信,并计算字母的出现次数
count_occurrences(List, Occ):-
findall([X,L], (bagof(true,member(X,List),Xs), length(Xs,L)), Occ).
但是它花费了太多的时间,还有什么其他的解决方案可以找到呢?即使使用Prolog,当N>4时暴力生成也会花费很多时间。当然,序言推理比“C”中的暴力强制慢 解决方案是智能生成:
- 从6个字符中选择4个字符,让它们出现2次
- 在剩余的部分中,让它们出现0或1次
- 可以限制引用的数量
generate(N, O) :-
getComponents(Xs),
combineComponents(Xs, Ys),
generateN(N, Ys, O).
getComponents([twice(A, B, C, D), zeroOrOne(X, Y)]) :-
Ls = [a, b, c, d, e, f],
member2(A, Ls, Rs),
member2(B, Rs, Ts),
member2(C, Ts, Ss),
member2(D, Ss, Xs),
Xs = [X, Y].
member2(X, [X|Rs], Rs).
member2(X, [F|Rs], [F|Ts]) :- member2(X, Rs, Ts).
combineComponents([twice(A, B, C, D), zeroOrOne(X, Y)], Out6) :-
twice(A, Out1, []),
twice(B, Out2, Out1),
twice(C, Out3, Out2),
twice(D, Out4, Out3),
zeroOrOne(X, Out5, Out4),
zeroOrOne(Y, Out6, Out5).
generateN(N, Ys, A) :-
append(A, _, Ys),
length(A, N).
twice(A, [A, A|R], R).
zeroOrOne(A, R, R).
zeroOrOne(A, [A|R], R).
有3个步骤:
[a,a,c,c,b,b,f,f]
[c,c,b,b,f,f,a,a]
所以这对你来说是一种锻炼 我试图使这个解决方案尽可能有效,至少对于一个晚上的小拼图来说是如此。欢迎提出改进建议 从字母表{a,b,c,d,e,f}生成长度为N的所有单词,其中4个字母出现两次,所有其他字母出现一次或根本不出现 我的理解是,这意味着任何解决方案的长度都是8到10:必须显示4*2个字符,并且最多还有两个其他可选字符。我的解决方案的结构是首先构造一个由这些可选字符组成的“基字”。然后,我们在这个基字中插入另外四个字符的两个副本 所以,这个双重插入首先:
insert2(Xs, Y) -->
[Y],
insert(Xs, Y).
insert2([X | Xs], Y) -->
[X],
insert2(Xs, Y).
insert(Xs, Y) -->
[Y],
list(Xs).
insert([X | Xs], Y) -->
[X],
insert(Xs, Y).
list([]) -->
[].
list([X | Xs]) -->
[X],
list(Xs).
例如:
?- phrase(insert2([b], a), List).
List = [a, a, b] ;
List = [a, b, a] ;
List = [b, a, a].
?- puzzle_(List).
List = [_2278, _2278, _2230, _2230, _2194, _2194, _2170, _2170] ;
List = [_2278, _2230, _2278, _2230, _2194, _2194, _2170, _2170] ;
List = [_2278, _2230, _2230, _2278, _2194, _2194, _2170, _2170] ;
List = [_2278, _2230, _2230, _2194, _2278, _2194, _2170, _2170] ;
List = [_2278, _2230, _2230, _2194, _2194, _2278, _2170, _2170] ;
List = [_2278, _2230, _2230, _2194, _2194, _2170, _2278, _2170] .
?- puzzle_(List), label(List).
List = [a, a, b, b, c, c, d, d] ;
List = [a, a, b, b, c, c, e, e] ;
List = [a, a, b, b, c, c, f, f] ;
List = [a, a, b, b, d, d, c, c] ;
List = [a, a, b, b, d, d, e, e] ;
List = [a, a, b, b, d, d, f, f] ;
List = [a, a, b, b, e, e, c, c] ;
List = [a, a, b, b, e, e, d, d] .
请注意,这是为了避免在末尾留下选择点(在SWI Prolog中)而精心编写的。更重要的是,它的编写也是为了避免重复的解决方案;如果您实现双重插入,即“插入到列表中,然后插入到该结果中”,您将得到重复的结果,在指数算法中,这些结果将受到影响
因此,基本词汇是:
baseword([]).
baseword([_X]).
baseword([_X, _Y]).
描述所有可能解的结构的谓词是:
puzzle_(CharacterPlaceholders) :-
baseword(BaseWord),
phrase(insert2(BaseWord, _A), WordWith2A),
phrase(insert2(WordWith2A, _B), WordWith2A2B),
phrase(insert2(WordWith2A2B, _C), WordWith2A2B2C),
phrase(insert2(WordWith2A2B2C, _D), WordWith2A2B2C2D),
CharacterPlaceholders = WordWith2A2B2C2D.
例如:
?- phrase(insert2([b], a), List).
List = [a, a, b] ;
List = [a, b, a] ;
List = [b, a, a].
?- puzzle_(List).
List = [_2278, _2278, _2230, _2230, _2194, _2194, _2170, _2170] ;
List = [_2278, _2230, _2278, _2230, _2194, _2194, _2170, _2170] ;
List = [_2278, _2230, _2230, _2278, _2194, _2194, _2170, _2170] ;
List = [_2278, _2230, _2230, _2194, _2278, _2194, _2170, _2170] ;
List = [_2278, _2230, _2230, _2194, _2194, _2278, _2170, _2170] ;
List = [_2278, _2230, _2230, _2194, _2194, _2170, _2278, _2170] .
?- puzzle_(List), label(List).
List = [a, a, b, b, c, c, d, d] ;
List = [a, a, b, b, c, c, e, e] ;
List = [a, a, b, b, c, c, f, f] ;
List = [a, a, b, b, d, d, c, c] ;
List = [a, a, b, b, d, d, e, e] ;
List = [a, a, b, b, d, d, f, f] ;
List = [a, a, b, b, e, e, c, c] ;
List = [a, a, b, b, e, e, d, d] .
枚举所有变体需要多长时间
?- time((puzzle_(CharacterPlaceholders), false)).
% 845,559 inferences, 0.040 CPU in 0.040 seconds (100% CPU, 21324130 Lips)
false.
看起来很合理
鉴于这些结构,仍然需要填充占位符。这包括将字母分配给列表中的变量(每个字母只分配一次,但如果变量在列表中出现两次,这将使字母也出现两次):
例如:
?- phrase(insert2([b], a), List).
List = [a, a, b] ;
List = [a, b, a] ;
List = [b, a, a].
?- puzzle_(List).
List = [_2278, _2278, _2230, _2230, _2194, _2194, _2170, _2170] ;
List = [_2278, _2230, _2278, _2230, _2194, _2194, _2170, _2170] ;
List = [_2278, _2230, _2230, _2278, _2194, _2194, _2170, _2170] ;
List = [_2278, _2230, _2230, _2194, _2278, _2194, _2170, _2170] ;
List = [_2278, _2230, _2230, _2194, _2194, _2278, _2170, _2170] ;
List = [_2278, _2230, _2230, _2194, _2194, _2170, _2278, _2170] .
?- puzzle_(List), label(List).
List = [a, a, b, b, c, c, d, d] ;
List = [a, a, b, b, c, c, e, e] ;
List = [a, a, b, b, c, c, f, f] ;
List = [a, a, b, b, d, d, c, c] ;
List = [a, a, b, b, d, d, e, e] ;
List = [a, a, b, b, d, d, f, f] ;
List = [a, a, b, b, e, e, c, c] ;
List = [a, a, b, b, e, e, d, d] .
分别枚举长度为8、9和10的所有解的成本是多少
?- between(8, 10, Length), length(List, Length), time((puzzle_(List), label(List), false)).
% 7,306,838 inferences, 0.334 CPU in 0.334 seconds (100% CPU, 21869592 Lips)
% 128,616,758 inferences, 6.052 CPU in 6.052 seconds (100% CPU, 21252001 Lips)
% 918,924,038 inferences, 46.998 CPU in 46.999 seconds (100% CPU, 19552521 Lips)
false.
对于这个问题,您希望使用Prolog“代码”来表示您的 不是序言中的“原子”这个词。 “代码”包含在双引号中,是语法上的糖类 要一份清单。 “代码”列表中的项目是与每个字符的unicode编号相对应的数字。 与“原子”相比,“代码”的巨大优势在于,您可以使用所有 可用于创建、检查、切片和切割列表的设施;其中最值得注意的是:DCG
:- system:set_prolog_flag(double_quotes,codes) .
:- [library(lists)] .
main(N0)
:-
main(N0,WORDz) ,
system:format('one possibility is "~s" .~n',[WORDz]) ,
fail ;
true
.
main(N0,WORDz)
:-
main("abcdef",N0,WORDz)
.
main(ALPHABETz0,N0,WORDz)
:-
constrain(N0,WORDz) ,
generate(ALPHABETz0,WORDz)
.
constrain(N0,WORDz0)
:-
prolog:length(WORDz0,N0)
.
generate(ALPHABETz0,WORDz)
:-
prolog:phrase(generate(ALPHABETz0),WORDz)
.
generate(ALPHABETz0)
-->
{
lists:select(A,ALPHABETz0,ALPHABETz1) ,
lists:select(B,ALPHABETz1,ALPHABETz2) ,
lists:select(C,ALPHABETz2,ALPHABETz3) ,
lists:select(D,ALPHABETz3,ALPHABETz4) ,
lists:select(E,ALPHABETz4,ALPHABETz5) ,
lists:select(F,ALPHABETz5,_LPHABETz6)
} ,
double(A) ,
double(B) ,
double(C) ,
double(D) ,
optional(E) ,
optional(F)
.
double(IT) --> [IT] , [IT] .
optional(IT) --> [IT] .
optional(_IT) --> [] .
我同意使用列表,但是为什么使用
code
而不是chars
。