可能な単語の四角形を見つける


8

ジョニーはクロスワードパズルを作成しようとしていますが、単語を互いに一致させることが困難です。

彼はいくつかの単純な単語の長方形を考え出しました。つまり、長方形を形成する単語のグループで、すべての水平および垂直のパスが単語を形成します。

//2x2 
PA
AM

//2x3
GOB
ORE

//3x3
BAG
AGO
RED

//3x4
MACE
AGES
WEES

ただし、優れたパズルを作成するには、3x4よりやや大きい単語の長方形が必要です。長い間コードの長いブロックがジョニーのようなカジュアルなプログラマーにとって非常に威圧的であるため、ジョニーは、アレンジメントの手紙を何時間も苦労する代わりに、彼のために、そしてできるだけ少ない文字でこれを行うプログラムを持つことを好みます。


与えられた

  • 単語がアルファベット順に改行で区切られているテキストファイル辞書
  • 四角形の単語の行と列の数を指定する入力(提供することができますが、選択したプログラミング言語で最も便利です)

少なくとも1つの単語の長方形を生成します。与えられたレキシコンとディメンションで単語の長方形を生成することができない場合、プログラムは動作を定義する必要はありません。プログラムが64文字を超える長方形、またはいずれかの方向に8を超える寸法を持つ長方形を生成できる必要はありません。プログラムは、妥当な時間内、たとえば30分以内に完了する必要があります。


編集:NxNの四角形を使用している場合は、N文字の長さの単語のみを含む小さな辞書ファイルを使用できます。


私が書いたコードをJava4kゲーム用に適合させることができるかもしれません。
Peter Taylor、

1
誰かがその単語リストから8時間8分以内に8x8を生成できる実装があるかどうか知りたいです。
MtnViewMark

@MtnViewMarkそうでない場合は、必要なサイズを減らしてもかまいません。そうであっても、可能性の数を減らすためにチェックを行うことができれば、キースの実装はかなり速くなると思います。
Peter Olson、

そして、すべての言葉は異なっている必要がありますか?
Ming-Tang

1
@MtnViewMarkでは、接頭辞が有効かどうかを単にテストするのではなく、各ノードの下の子の数を格納するトライを使用して、継続が最も可能性が高い解を与えるヒューリスティックを提供します。(次に、ヒューリスティックによって重み付けされたランダムに1つを選択します。結果のばらつきは仕様に明示されていないため、おそらく最良の候補を選択することをお勧めします)。接頭辞ではなくグローバルにヒューリスティックスを適用することに基づいて、カバーを設定するための削減をテストし、Knuthのダンスリンクで解決しますが、これまでのところ有望ではありません。多分もっと良い削減があるでしょう。
Peter Taylor、

回答:


5

Haskell、586文字

import Data.List
import qualified Data.Vector as V
import System
data P=P[String](V.Vector P)
e=P[]$V.replicate 128 e
(a:z)∈(P w v)|a>' '=z∈(V.!)v(fromEnum a);_∈(P w _)=w
pw=q p w where q(P u v)=P(w:u).V.accum q v.x;x(a:z)=[(fromEnum a,z)];x _=[]
l%n=foldl'(∫)e.filter((==n).length)$l
d§(p,q:r)=map(\w->p++w:r)$q∈d;_§(p,_)=[p]
(d¶e)i=filter(not.any(null.(∈e))).map transpose.(d§).splitAt i
s i n d e m|i==n=["":m]|1<3=(d¶e)i m>>=(e¶d)i>>=s(i+1)n d e
p[b,a,n]w=take n$s 0(a`max`b)(w%a)(w%b)$replicate b$replicate a ' '
main=do{g<-map read`fmap`getArgs;interact$unlines.concat.p g.lines}

3つの引数を指定することによって呼び出されます:行数、列数、ソリューション数。単語リストは次で受け入れられますstdin

$> ghc -O3 2554-WordRect.hs 
[1 of 1] Compiling Main             ( 2554-WordRect.hs, 2554-WordRect.o )
Linking 2554-WordRect ...

$> time ./2554-WordRect 7 7 1 < 2554-words.txt

zosters
overlet
seriema
trimmer
element
remends
startsy

real    0m22.381s
user    0m22.094s
sys     0m0.223s

ご覧のとおり、7×7は比較的高速に実行されます。まだタイミング8×8と7×6 ...

解の数の引数を削除してすべての解を生成する場合は9文字短くなりますが、時間を計ることができなくなります。


  • 編集:(585→455)カスタムデータ構造を、可能な置換へのプレフィックス文字列の単純なマップに置き換えました。奇妙なことに、これは少し遅いです。おそらくMap String a、手作りの木よりも遅いためですMap Char a...
  • 編集:(455→586)大きい?!?!! このバージョンでは、元のソリューションとpythonおよびawkソリューションの両方の手法を使用して、検索スペースの最適化がさらに進んでいます。さらに、に基づくカスタムデータ構造Vectorは、単純なを使用するよりもはるかに高速ですMap。なぜこれを行うのですか?半時間以内に8x8の目標に近いソリューションが短いソリューションよりも望ましいと思うので。

1
GHCのどのバージョンを使用していますか?--make質問に投稿された単語リストで同じコマンド(ghcの後で必要になることを除く)を使用すると、「zymesz」や「youthy」など、リストにない単語が表示されます。
Joey Adams、

2
おそらく、出力グリッドが独自の転置であってはならないという要件を変更する必要があります。
Peter Taylor、

1
@Joey:words.txtファイルをローカルの行末に変換しましたか?Mac OS Xで実行していて、使用する前にファイルを\ n行末に変換しました。
MtnViewMark

ありがとう、うまくいきました。入力ファイルは小文字なければならず、システムと互換性のある行末持っている必要があります。
Joey Adams、

5

Python、232文字

x,y=input()
H=[]
P={}
for w in open('words.txt'):
 l=len(w)-2
 if l==x:H+=[w]
 if l==y:
  for i in range(y+1):P[w[:i]]=1
def B(s):
 if(x+2)*y-len(s):[B(s+w)for w in H if all((s+w)[i::x+2]in P for i in range(x))]
 else:print s
B('')

ただし、1/2時間の制限では6x6までしか処理できません。


1
すべてのペアまたは有効な単語のペアのみを生成しますか?上から下に縦に読むと、有効な単語になっていないようです。たとえば、「aahs」、「abet」、「lack」という1つの結果が得られましたが、縦書きの「stk」は単語リストにありません。また、上記の場合、パラメーターを3,3返しますが、その戻り単語3x4
YOU

うーん、それは私が得るものではありません。問題は辞書の改行にあると思います。提供されたファイルにはWindowsの改行(\ r \ n)が含まれているため、単語の長さはlen(w)-2です。改行を何らかの方法で変換した場合(またはWindowsのPythonで変換した場合)、それをlen(w)-1に変更すると修正されます。
Keith Randall、

...他+2のをに変更し+1ます。
Keith Randall、

ああ、分かった。WindowsとLinuxでテストしました。WindowsのPythonは\rから自動的に削除されw、LinuxではファイルをUnix形式に変換したため、どちらも機能しませんでした。
YOU

これは非常にエレガントなソリューションです。
asoundmove

3

Java(1065バイト)

import java.util.*;public class W{public static void main(String[]a){new
W(Integer.parseInt(a[0]),Integer.parseInt(a[1]));}W(int w,int h){M
H=new M(),V=new M();String L;int i,j,l,m,n=w*h,p[]=new int[n];long
I,J,K,M,C=31;long[]G=new long[h],T=new long[w],W[]=new long[n][],X;try{Scanner
S=new Scanner(new java.io.File("words.txt"));while(0<1){L=S.nextLine();l=L.length();for(i=0;i>>l<1;i++){K=0;for(j=0;j<l;j++)K+=(i>>j&1)*(L.charAt(j)-96L)<<5*j;if(l==w)H.put(K,H.g(K)+1);if(l==h)V.put(K,V.g(K)+1);}}}catch(Exception
E){}while(n-->0){j=1;if(W[n]==null){M=1L<<62;for(i=w*h;i-->0;){m=i/w;l=i%w*5;if((G[m]>>l&C)<1){X=new
long[27];I=K=0;for(;K++<26;){J=H.g(G[m]+(K<<l))*V.g(T[i%w]+(K<<5*m));X[(int)K]=K-32*J;I+=J;}if(I<1)j=0;if(I<M){M=I;p[n]=i;W[n]=X;}}}}X=W[n];Arrays.sort(X);M=X[0]*j;X[0]=0;K=M&C;i=p[n]%w;j=p[n]/w;l=5*i;m=5*j;G[j]&=~(C<<l);G[j]+=K<<l;T[i]&=~(C<<m);T[i]+=K<<m;if(M>=0){W[n]=null;n+=2;}}for(long
A:G){L="";for(i=0;i<w;)L+=(char)(96+(C&A>>5*i++));System.out.println(L);}}class
M extends HashMap<Long,Long>{long g(Long s){return get(s)!=null?get(s):0;}}}

最短になるには長い道のりですが、タイミングの制約を満たすのに最も近いと思います。入力ファイルが適切な長さの単語にフィルターされていると想定して、14バイトを節約しました。私のネットブックでは、全体をフィードする場合words.txt、最初の1分間を費やして前処理を行い、生成したもののほとんどを破棄します。その後、7x7を解くのにわずか20秒ほどかかります。私のデスクトップでは、15秒未満ですべてが実行され、次のようになります。

rascals
areolae
serrate
coroner
alanine
latents
seeress

8x7または8x8の解決策を見つけることなく、50時間以上実行しました。8文字の単語は、この問題の重要な境界線のように見えます-それは、あまり進歩せずに半分いっぱいにホバリングするだけです。

使用されるアプローチは、完全なピボットであり、可能な水平補完の数に垂直補完の数を掛けたものに基づくヒューリスティックです。たとえば、中間グリッドがある場合

*ean*
algae
*ar**
*ier*
*nee*

次に、左上隅にヒューリスティック値を指定しcount(aean*)count(aa***) + count(bean*)count(ba***) + ... + count(zean*)count(za***)ます。すべてのセルの中で、ヒューリスティック値が最も小さい(つまり、満足させるのが難しい)セルを選択し、そのセルのヒューリスティック値に寄与した量の降順で(つまり、成功する可能性が最も高いセルから)文字を処理します。 )。


素敵なアプローチと説明。
MtnViewMark 2011年

2

F#

バックトラックソリューションですが、後で検索スペースを最適化します。

open System

(*-NOTES
    W=7 H=3
    abcdefg<-- searching from wordsW
    abcdefg
    abcdefg
    ^
    partial filtering from wordsH
  *)

let prefix (s : char[]) (a : char[]) =
  a.[0..s.Length - 1] = s

let filterPrefix (s : char[]) =
  Array.filter (prefix s)

let transpose (s : char[][]) =
  [|
    for y = 0 to s.[0].Length - 1 do
      yield [|
        for x = 0 to s.Length - 1 do
          yield s.[x].[y]
      |]
  |]

[<EntryPoint>]
let main (args : String[]) =
  let filename, width, height = "C:\Users\AAA\Desktop\words.txt", 3, 3
  let lines = System.IO.File.ReadAllLines filename |> Array.map (fun x -> x.ToCharArray())
  let wordsW = Array.filter (Array.length >> ((=) width)) lines
  let wordsH = Array.filter (Array.length >> ((=) height)) lines

  let isValid (partial : char[][]) =
    if partial.Length = 0 then
      true
    else
      seq {
        for part in transpose partial do
          yield Seq.exists (prefix part) wordsH
      }
      |> Seq.reduce (&&)

  let slns = ref []
  let rec back (sub : char[][]) =
    if isValid sub then
      if sub.Length = height then
        slns := sub :: !slns
      else
        for word in wordsW do
          back <| Array.append sub [| word |]

  back [| |]
  printfn "%A" !slns
  0

1

awk、283

(パラメーター入力フラグ用に14を追加する必要がある場合があります)

例:awk -v x=2 -v y=2...
最初に一致するものを見つけて印刷する(283文字):

{if(x==L=length)w[++n]=$0;if(y==L)for(;L>0;L--)W[substr($0,1,L)]++}END{for(i[Y=1]++;i[j=1]<=n;){b[Y]=w[i[Y]];while(j<=x){s="";for(k=1;k<=Y;k++)s=s substr(b[k],j,1);W[s]?0:j=x;j++}if(W[s])if(Y-y)i[++Y]=0;else{for(k=1;k<=Y;k++)print b[k];exit}i[Y]++;while(Y>1&&i[Y]>n)i[--Y]++}print N}

一致する数を見つける(245文字、はるかに遅い):

{if(x==L=length)w[++n]=$0;if(y==L)for(;L>0;L--)W[substr($0,1,L)]++}END{for(i[Y=1]++;i[j=1]<=n;){b[Y]=w[i[Y]];while(j<=x){s="";for(k=1;k<=Y;k++)s=s substr(b[k],j,1);W[s]?0:j=x;j++}W[s]?Y-y?i[++Y]=0:N++:0;i[Y]++;while(Y>1&&i[Y]>n)i[--Y]++}print N}

両方のプログラムの場合(もちろん、ソリューションカウントの場合はさらに多い)、xとyの値によっては、実行時間が30分をはるかに超えます。

関心事として、ここに各単語長の単語数を示します。

 2     85
 3    908
 4   3686
 5   8258
 6  14374
 7  21727
 8  26447
 9  16658
10   9199
11   5296
12   3166
13   1960
14   1023
15    557
16    261
17    132
18     48
19     16
20      5
21      3
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.