Dr.*_*ius 12 wolfram-mathematica pattern-matching crossword
假设我从Mathematica字典中选择所有3个字符:
all3 = Characters /@ Select[DictionaryLookup[], StringLength[#] == 3 &];
Run Code Online (Sandbox Code Playgroud)
我想形成完整的拼字游戏集,例如:
A B E
R A Y
E R E
Run Code Online (Sandbox Code Playgroud)
可以水平和垂直读取单词.
显然,可以通过递归和回溯找到集合.但:
1)有没有办法用模式解决它?
2)哪些尺寸有有效的解决方案?
编辑
我写这个问题DictionaryLookup[]只是因为它是一个合理大小的可变长度记录数据库.我的真正问题与字典查找无关,而与某种织机模式有关.
Jan*_*nus 11
我不确定你是否会考虑以下方法模式 - 但它可以工作,并且它可以想象地扩展到许多维度,虽然使用all3数据集,它可能会很早就开始......
我们的想法是从一个空白的填字游戏开始:
blankCW={{_,_,_},{_,_,_},{_,_,_}};
Run Code Online (Sandbox Code Playgroud)
然后递归执行以下操作:对于给定的模式,依次查看行和(在填写任何完成一个完成后)以最少的匹配数展开行上的模式:
(* Cache the number of matches for a given pattern *)
nmatch[patt_]:=nmatch[Verbatim@patt]=Length@Cases[all3,patt]
(* A helper to fill single matches if needed *)
fixone[ml_,nl_]:=If[FreeQ[ml[[nl]],Verbatim[_]],ml,
ReplacePart[ml, nl->First@Cases[all3,ml[[nl]]]]];
findCompletions[m_]:=Module[{nn,ur},
(* Pattern w/ filled single matches -> ur, ordering by # of matches -> nn *)
{ur,nn}=NestWhile[{fixone[#[[1]],First@#[[2]]], Rest@#[[2]]}&,
{m,Ordering[nmatch/@m]},
(Length[#[[2]]]>0&&nmatch@#[[1,#[[2,1]]]]==1)&];
(* Expand on the word with the fewest number og matches *)
If[Length[nn]==0,{ur},
With[{n=First@nn},ReplacePart[ur,n-> #]&/@Cases[all3,ur[[n]]]]]];
Run Code Online (Sandbox Code Playgroud)
对于给定的候选模式,尝试沿两个维度完成并保持产生最少的那个:
findCompletionsOriented[m_]:=Module[{osc},
osc=findCompletions/@Union[{m,Transpose@m}];
osc[[First@Ordering[Length/@osc,1]]]]
Run Code Online (Sandbox Code Playgroud)
我首先做了递归广度以便能够使用Union,但是对于更大的问题,可能需要深度优先.性能是如此:在示例问题中找到116568匹配的8分钟笔记本电脑:
Timing[crosswords=FixedPoint[Union[Join@@(findCompletionsOriented/@#)]&,{blankCW}];]
Length@crosswords
TableForm/@Take[crosswords,5]
Out[83]= {472.909,Null}
Out[84]= 116568
aah aah aah aah aah
Out[86]={ ace ace ace ace ace }
hem hen hep her hes
Run Code Online (Sandbox Code Playgroud)
原则上,应该可以将其递归到更高的维度,即使用填字词列表而不是维度3的词汇表.如果匹配列表的模式的时间在列表长度中是线性的,那么这将是非常慢的拥有100000多个大小的词汇表......
另一种方法是使用SatisfiabilityInstances约束指定每行和每列必须是有效字.下面的代码需要40秒才能使用200个三字母单词的字典获得前5个解决方案.你可以代替SatisfiabilityInstances用SatisfiabilityCount得到这样的填字游戏的数量.
setupCrossword[wordStrings_] := (
m = Length[chars];
words = Characters /@ wordStrings;
chars = Union@Flatten@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];
Framed@TableForm@decodeInstance@# & /@ vals
Run Code Online (Sandbox Code Playgroud)
http://yaroslavvb.com/upload/save/crosswords.png
这种方法使用变量{{i,j},"c"}来表示单元格{i,j}得到字母"c".约束的每个单元格都只有一个字母BooleanCountingFunction,每个行和列都被约束为一个有效的单词.例如,第一行必须是"ace"或"bar"的约束看起来像这样
{{1,1},"a"}&&{{1,2},"c"}&&{{1,3},"e"}||{{1,1},"b"}&&{{1,2},"a"}&&{{1,3},"r"}
Run Code Online (Sandbox Code Playgroud)