另一种方法是使用SatisfiabilityInstances
,约束指定每行和每列必须是有效的单词。下面的代码需要40秒才能获得使用200个三字母词典的前5个解决方案。您可以用SatisfiabilityCount
替换SatisfiabilityInstances
以获得这些填字游戏的数量。
setupCrossword[wordStrings_] := (
m = Length[chars];
words = Characters /@ wordStrings;
chars = [email protected]@words;
wordMatch[vars_, word_] := And @@ (Thread[{vars, word}]);
validWord[vars_] := Or @@ (wordMatch[vars, #] & /@ words);
validCell[{i_, j_}] :=
BooleanCountingFunction[{1}, {{i, j}, #} & /@ chars];
row[i_] := {i, #} & /@ Range[n];
col[i_] := {#, i} & /@ Range[n];
cells = Flatten[row /@ Range[n], 1];
rowCons = validWord[row[#]] & /@ Range[n];
colCons = validWord[col[#]] & /@ Range[n];
cellCons = validCell /@ cells;
formula = And @@ (Join[rowCons, colCons, cellCons]);
vars =
Table[{{i, j}, c}, {i, 1, n}, {j, 1, n}, {c, chars}] //
Flatten[#, 2] &;
decodeInstance[instance_] := (
choices = Extract[vars, Position[instance, True]];
grid = Table[{i, j}, {i, 1, n}, {j, 1, n}] /. Rule @@@ choices
)
);
n = 3;
wordLimit = 200;
wordStrings =
Select[DictionaryLookup[],
StringLength[#] == n && LowerCaseQ[#] &];
setupCrossword[wordStrings[[;; wordLimit]]];
vals = SatisfiabilityInstances[formula, vars, 5];
[email protected]@[email protected]# & /@ vals
http://yaroslavvb.com/upload/save/crosswords.png
这种方法使用变量,如{{i,j},"c"}
指示细胞{i,j}
得到字母 “C”。每个单元格受约束只能得到一个字母BooleanCountingFunction
,每行和每列都被约束成一个有效的单词。例如,约束第一行必须是“王牌”或“ - ”看起来像这样
{{1,1},"a"}&&{{1,2},"c"}&&{{1,3},"e"}||{{1,1},"b"}&&{{1,2},"a"}&&{{1,3},"r"}
感谢您的努力!我以前从来没有使用过** SatisfiabilityInstances **,虽然我看到你在过去发布的那些很好的四面体问题中使用过它。我想这一个会花一些时间来咀嚼:D – 2011-02-01 22:58:38
好主意!我认为模式匹配是一个死路一条:即使在调度表中,我也无法检查每秒超过一百万的候选人 - 这意味着整个问题超过一个小时。 – Janus 2011-02-02 04:06:07