मुझे लगता है कि आप शायद अपना अधिकतर समय उन शब्दों से मेल खाने की कोशिश करेंगे जो संभवतः आपके पत्र ग्रिड द्वारा नहीं बनाए जा सकते हैं। इसलिए, पहली बात यह है कि मैं उस कदम को तेज करने की कोशिश करूंगा और आपको वहां सबसे ज्यादा रास्ता तय करना चाहिए।
इसके लिए, मैं ग्रिड को संभावित "चाल" की एक तालिका के रूप में फिर से व्यक्त करूंगा जो आप उस पत्र-संक्रमण द्वारा अनुक्रमित करते हैं जिसे आप देख रहे हैं।
प्रत्येक अक्षर को अपने पूरे वर्णमाला (ए = 0, बी = 1, सी = 2, ... और आगे से) का एक नंबर निर्दिष्ट करके शुरू करें।
आइए इस उदाहरण को लेते हैं:
h b c d
e e g h
l l k l
m o f p
और अभी के लिए, हमारे पास मौजूद अक्षरों की वर्णमाला का उपयोग करने देता है (आमतौर पर आप हर बार उसी संपूर्ण वर्णमाला का उपयोग करना चाहेंगे):
b | c | d | e | f | g | h | k | l | m | o | p
---+---+---+---+---+---+---+---+---+---+----+----
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11
फिर आप एक 2 डी बूलियन सरणी बनाते हैं जो बताता है कि क्या आपके पास एक निश्चित पत्र संक्रमण उपलब्ध है:
| 0 1 2 3 4 5 6 7 8 9 10 11 <- from letter
| b c d e f g h k l m o p
-----+--------------------------------------
0 b | T T T T
1 c | T T T T T
2 d | T T T
3 e | T T T T T T T
4 f | T T T T
5 g | T T T T T T T
6 h | T T T T T T T
7 k | T T T T T T T
8 l | T T T T T T T T T
9 m | T T
10 o | T T T T
11 p | T T T
^
to letter
अब अपनी शब्द सूची पर जाएं और शब्दों को संक्रमण में बदलें:
hello (6, 3, 8, 8, 10):
6 -> 3, 3 -> 8, 8 -> 8, 8 -> 10
फिर जांचें कि क्या ये परिवर्तन आपकी तालिका में देखने की अनुमति है:
[6][ 3] : T
[3][ 8] : T
[8][ 8] : T
[8][10] : T
यदि वे सभी अनुमत हैं, तो एक मौका है कि यह शब्द मिल सकता है।
उदाहरण के लिए "हेलमेट" शब्द को 4 के संक्रमण (m से e: helMEt) पर खारिज किया जा सकता है, क्योंकि आपकी तालिका में प्रविष्टि झूठी है।
और हम्सटर शब्द से इंकार किया जा सकता है, क्योंकि पहली (एच से) संक्रमण की अनुमति नहीं है (आपकी तालिका में भी मौजूद नहीं है)।
अब, शायद बहुत कम शेष शब्दों के लिए जिन्हें आपने समाप्त नहीं किया, वास्तव में उन्हें ग्रिड में खोजने का प्रयास करें जिस तरह से आप अभी कर रहे हैं या जैसा कि यहां कुछ अन्य उत्तरों में सुझाया गया है। यह झूठी सकारात्मकता से बचने के लिए है जो आपके ग्रिड में समान अक्षरों के बीच कूदता है। उदाहरण के लिए "मदद" शब्द को तालिका द्वारा अनुमति दी गई है, लेकिन ग्रिड द्वारा नहीं।
इस विचार पर कुछ और प्रदर्शन सुधार युक्तियाँ:
2 डी सरणी का उपयोग करने के बजाय, 1 डी सरणी का उपयोग करें और बस दूसरे अक्षर के सूचकांक की गणना स्वयं करें। इसलिए, ऊपर दिए गए 12x12 सरणी के बजाय, 1D लंबाई की 144 लंबाई बनाएं। यदि आप हमेशा एक ही वर्णमाला का उपयोग करते हैं (यानी मानक अंग्रेजी वर्णमाला के लिए एक 26x26 = 676x1 सरणी), भले ही सभी पत्र आपके ग्रिड में दिखाई न दें। , आप सूचक को इस 1D सरणी में पूर्व-गणना कर सकते हैं जिसे आपको अपने शब्दकोश शब्दों से मिलान करने के लिए परीक्षण करने की आवश्यकता है। उदाहरण के लिए, ऊपर के उदाहरण में 'हैलो' के लिए संकेत होगा
hello (6, 3, 8, 8, 10):
42 (from 6 + 3x12), 99, 104, 128
-> "hello" will be stored as 42, 99, 104, 128 in the dictionary
विचार को एक 3 डी तालिका में विस्तारित करें (1 डी सरणी के रूप में व्यक्त किया गया), अर्थात सभी 3-अक्षर संयोजनों की अनुमति है। इस तरह से आप और भी अधिक शब्दों को तुरंत समाप्त कर सकते हैं और आप प्रत्येक शब्द के लिए सरणी लुकअप की संख्या को 1 से कम कर देते हैं: 'हैलो' के लिए, आपको केवल 3 सरणी लुक की आवश्यकता होगी: हेल, ईएलएल, ल्लो। इस तालिका का निर्माण करना बहुत जल्दी होगा, वैसे, आपके ग्रिड में केवल 400 संभव 3-अक्षर-चालें हैं।
अपने ग्रिड में उन चालों के सूचकांकों की पूर्व-गणना करें जिन्हें आपको अपनी तालिका में शामिल करने की आवश्यकता है। उपरोक्त उदाहरण के लिए, आपको निम्नलिखित प्रविष्टियों को 'ट्रू' में सेट करना होगा:
(0,0) (0,1) -> here: h, b : [6][0]
(0,0) (1,0) -> here: h, e : [6][3]
(0,0) (1,1) -> here: h, e : [6][3]
(0,1) (0,0) -> here: b, h : [0][6]
(0,1) (0,2) -> here: b, c : [0][1]
.
:
- इसके अलावा 16 प्रविष्टियों के साथ 1-डी सरणी में अपने खेल ग्रिड का प्रतिनिधित्व करते हैं और तालिका 3 पूर्व में गणना की है। इस सरणी में सूचक होते हैं।
मुझे यकीन है कि यदि आप इस दृष्टिकोण का उपयोग करते हैं तो आप अपने कोड को पागलपन से तेजी से चलाने के लिए प्राप्त कर सकते हैं, यदि आपके पास शब्दकोश पहले से गणना की गई है और पहले से ही मेमोरी में लोड है।
BTW: एक और अच्छी बात है, अगर आप एक गेम बना रहे हैं, तो इस तरह की चीजों को तुरंत बैकग्राउंड में चलाना है। पहला गेम बनाना और हल करना शुरू करें जबकि उपयोगकर्ता अभी भी आपके ऐप पर शीर्षक स्क्रीन को देख रहा है और "प्ले" दबाने के लिए अपनी उंगली को स्थिति में ला रहा है। फिर अगला गेम जेनरेट करें और हल करें क्योंकि उपयोगकर्ता पहले वाला खेल खेलता है। आपको अपना कोड चलाने के लिए बहुत समय देना चाहिए।
(मुझे यह समस्या पसंद है, इसलिए मैं शायद अगले दिनों में जावा में अपना प्रस्ताव लागू करने के लिए लुभाऊंगा, यह देखने के लिए कि यह वास्तव में कैसा प्रदर्शन करेगा ... मैं एक बार यहां कोड पोस्ट करूंगा।
अपडेट करें:
ठीक है, मेरे पास आज कुछ समय था और मैंने इस विचार को जावा में लागू किया:
class DictionaryEntry {
public int[] letters;
public int[] triplets;
}
class BoggleSolver {
// Constants
final int ALPHABET_SIZE = 5; // up to 2^5 = 32 letters
final int BOARD_SIZE = 4; // 4x4 board
final int[] moves = {-BOARD_SIZE-1, -BOARD_SIZE, -BOARD_SIZE+1,
-1, +1,
+BOARD_SIZE-1, +BOARD_SIZE, +BOARD_SIZE+1};
// Technically constant (calculated here for flexibility, but should be fixed)
DictionaryEntry[] dictionary; // Processed word list
int maxWordLength = 0;
int[] boardTripletIndices; // List of all 3-letter moves in board coordinates
DictionaryEntry[] buildDictionary(String fileName) throws IOException {
BufferedReader fileReader = new BufferedReader(new FileReader(fileName));
String word = fileReader.readLine();
ArrayList<DictionaryEntry> result = new ArrayList<DictionaryEntry>();
while (word!=null) {
if (word.length()>=3) {
word = word.toUpperCase();
if (word.length()>maxWordLength) maxWordLength = word.length();
DictionaryEntry entry = new DictionaryEntry();
entry.letters = new int[word.length() ];
entry.triplets = new int[word.length()-2];
int i=0;
for (char letter: word.toCharArray()) {
entry.letters[i] = (byte) letter - 65; // Convert ASCII to 0..25
if (i>=2)
entry.triplets[i-2] = (((entry.letters[i-2] << ALPHABET_SIZE) +
entry.letters[i-1]) << ALPHABET_SIZE) +
entry.letters[i];
i++;
}
result.add(entry);
}
word = fileReader.readLine();
}
return result.toArray(new DictionaryEntry[result.size()]);
}
boolean isWrap(int a, int b) { // Checks if move a->b wraps board edge (like 3->4)
return Math.abs(a%BOARD_SIZE-b%BOARD_SIZE)>1;
}
int[] buildTripletIndices() {
ArrayList<Integer> result = new ArrayList<Integer>();
for (int a=0; a<BOARD_SIZE*BOARD_SIZE; a++)
for (int bm: moves) {
int b=a+bm;
if ((b>=0) && (b<board.length) && !isWrap(a, b))
for (int cm: moves) {
int c=b+cm;
if ((c>=0) && (c<board.length) && (c!=a) && !isWrap(b, c)) {
result.add(a);
result.add(b);
result.add(c);
}
}
}
int[] result2 = new int[result.size()];
int i=0;
for (Integer r: result) result2[i++] = r;
return result2;
}
// Variables that depend on the actual game layout
int[] board = new int[BOARD_SIZE*BOARD_SIZE]; // Letters in board
boolean[] possibleTriplets = new boolean[1 << (ALPHABET_SIZE*3)];
DictionaryEntry[] candidateWords;
int candidateCount;
int[] usedBoardPositions;
DictionaryEntry[] foundWords;
int foundCount;
void initializeBoard(String[] letters) {
for (int row=0; row<BOARD_SIZE; row++)
for (int col=0; col<BOARD_SIZE; col++)
board[row*BOARD_SIZE + col] = (byte) letters[row].charAt(col) - 65;
}
void setPossibleTriplets() {
Arrays.fill(possibleTriplets, false); // Reset list
int i=0;
while (i<boardTripletIndices.length) {
int triplet = (((board[boardTripletIndices[i++]] << ALPHABET_SIZE) +
board[boardTripletIndices[i++]]) << ALPHABET_SIZE) +
board[boardTripletIndices[i++]];
possibleTriplets[triplet] = true;
}
}
void checkWordTriplets() {
candidateCount = 0;
for (DictionaryEntry entry: dictionary) {
boolean ok = true;
int len = entry.triplets.length;
for (int t=0; (t<len) && ok; t++)
ok = possibleTriplets[entry.triplets[t]];
if (ok) candidateWords[candidateCount++] = entry;
}
}
void checkWords() { // Can probably be optimized a lot
foundCount = 0;
for (int i=0; i<candidateCount; i++) {
DictionaryEntry candidate = candidateWords[i];
for (int j=0; j<board.length; j++)
if (board[j]==candidate.letters[0]) {
usedBoardPositions[0] = j;
if (checkNextLetters(candidate, 1, j)) {
foundWords[foundCount++] = candidate;
break;
}
}
}
}
boolean checkNextLetters(DictionaryEntry candidate, int letter, int pos) {
if (letter==candidate.letters.length) return true;
int match = candidate.letters[letter];
for (int move: moves) {
int next=pos+move;
if ((next>=0) && (next<board.length) && (board[next]==match) && !isWrap(pos, next)) {
boolean ok = true;
for (int i=0; (i<letter) && ok; i++)
ok = usedBoardPositions[i]!=next;
if (ok) {
usedBoardPositions[letter] = next;
if (checkNextLetters(candidate, letter+1, next)) return true;
}
}
}
return false;
}
// Just some helper functions
String formatTime(long start, long end, long repetitions) {
long time = (end-start)/repetitions;
return time/1000000 + "." + (time/100000) % 10 + "" + (time/10000) % 10 + "ms";
}
String getWord(DictionaryEntry entry) {
char[] result = new char[entry.letters.length];
int i=0;
for (int letter: entry.letters)
result[i++] = (char) (letter+97);
return new String(result);
}
void run() throws IOException {
long start = System.nanoTime();
// The following can be pre-computed and should be replaced by constants
dictionary = buildDictionary("C:/TWL06.txt");
boardTripletIndices = buildTripletIndices();
long precomputed = System.nanoTime();
// The following only needs to run once at the beginning of the program
candidateWords = new DictionaryEntry[dictionary.length]; // WAAAY too generous
foundWords = new DictionaryEntry[dictionary.length]; // WAAAY too generous
usedBoardPositions = new int[maxWordLength];
long initialized = System.nanoTime();
for (int n=1; n<=100; n++) {
// The following needs to run again for every new board
initializeBoard(new String[] {"DGHI",
"KLPS",
"YEUT",
"EORN"});
setPossibleTriplets();
checkWordTriplets();
checkWords();
}
long solved = System.nanoTime();
// Print out result and statistics
System.out.println("Precomputation finished in " + formatTime(start, precomputed, 1)+":");
System.out.println(" Words in the dictionary: "+dictionary.length);
System.out.println(" Longest word: "+maxWordLength+" letters");
System.out.println(" Number of triplet-moves: "+boardTripletIndices.length/3);
System.out.println();
System.out.println("Initialization finished in " + formatTime(precomputed, initialized, 1));
System.out.println();
System.out.println("Board solved in "+formatTime(initialized, solved, 100)+":");
System.out.println(" Number of candidates: "+candidateCount);
System.out.println(" Number of actual words: "+foundCount);
System.out.println();
System.out.println("Words found:");
int w=0;
System.out.print(" ");
for (int i=0; i<foundCount; i++) {
System.out.print(getWord(foundWords[i]));
w++;
if (w==10) {
w=0;
System.out.println(); System.out.print(" ");
} else
if (i<foundCount-1) System.out.print(", ");
}
System.out.println();
}
public static void main(String[] args) throws IOException {
new BoggleSolver().run();
}
}
यहाँ कुछ परिणाम हैं:
मूल प्रश्न (DGHI ...) में पोस्ट की गई तस्वीर से ग्रिड के लिए:
Precomputation finished in 239.59ms:
Words in the dictionary: 178590
Longest word: 15 letters
Number of triplet-moves: 408
Initialization finished in 0.22ms
Board solved in 3.70ms:
Number of candidates: 230
Number of actual words: 163
Words found:
eek, eel, eely, eld, elhi, elk, ern, erupt, erupts, euro
eye, eyer, ghi, ghis, glee, gley, glue, gluer, gluey, glut
gluts, hip, hiply, hips, his, hist, kelp, kelps, kep, kepi
kepis, keps, kept, kern, key, kye, lee, lek, lept, leu
ley, lunt, lunts, lure, lush, lust, lustre, lye, nus, nut
nuts, ore, ort, orts, ouph, ouphs, our, oust, out, outre
outs, oyer, pee, per, pert, phi, phis, pis, pish, plus
plush, ply, plyer, psi, pst, pul, pule, puler, pun, punt
punts, pur, pure, puree, purely, pus, push, put, puts, ree
rely, rep, reply, reps, roe, roue, roup, roups, roust, rout
routs, rue, rule, ruly, run, runt, runts, rupee, rush, rust
rut, ruts, ship, shlep, sip, sipe, spue, spun, spur, spurn
spurt, strep, stroy, stun, stupe, sue, suer, sulk, sulker, sulky
sun, sup, supe, super, sure, surely, tree, trek, trey, troupe
troy, true, truly, tule, tun, tup, tups, turn, tush, ups
urn, uts, yeld, yelk, yelp, yelps, yep, yeps, yore, you
your, yourn, yous
मूल प्रश्न में उदाहरण के रूप में पोस्ट किए गए पत्रों के लिए (FXIE ...)
Precomputation finished in 239.68ms:
Words in the dictionary: 178590
Longest word: 15 letters
Number of triplet-moves: 408
Initialization finished in 0.21ms
Board solved in 3.69ms:
Number of candidates: 87
Number of actual words: 76
Words found:
amble, ambo, ami, amie, asea, awa, awe, awes, awl, axil
axile, axle, boil, bole, box, but, buts, east, elm, emboli
fame, fames, fax, lei, lie, lima, limb, limbo, limbs, lime
limes, lob, lobs, lox, mae, maes, maw, maws, max, maxi
mesa, mew, mewl, mews, mil, mile, milo, mix, oil, ole
sae, saw, sea, seam, semi, sew, stub, swam, swami, tub
tubs, tux, twa, twae, twaes, twas, uts, wae, waes, wamble
wame, wames, was, wast, wax, west
निम्नलिखित 5x5-ग्रिड के लिए:
R P R I T
A H H L N
I E T E P
Z R Y S G
O G W E Y
यह देता है:
Precomputation finished in 240.39ms:
Words in the dictionary: 178590
Longest word: 15 letters
Number of triplet-moves: 768
Initialization finished in 0.23ms
Board solved in 3.85ms:
Number of candidates: 331
Number of actual words: 240
Words found:
aero, aery, ahi, air, airt, airth, airts, airy, ear, egest
elhi, elint, erg, ergo, ester, eth, ether, eye, eyen, eyer
eyes, eyre, eyrie, gel, gelt, gelts, gen, gent, gentil, gest
geste, get, gets, gey, gor, gore, gory, grey, greyest, greys
gyre, gyri, gyro, hae, haet, haets, hair, hairy, hap, harp
heap, hear, heh, heir, help, helps, hen, hent, hep, her
hero, hes, hest, het, hetero, heth, hets, hey, hie, hilt
hilts, hin, hint, hire, hit, inlet, inlets, ire, leg, leges
legs, lehr, lent, les, lest, let, lethe, lets, ley, leys
lin, line, lines, liney, lint, lit, neg, negs, nest, nester
net, nether, nets, nil, nit, ogre, ore, orgy, ort, orts
pah, pair, par, peg, pegs, peh, pelt, pelter, peltry, pelts
pen, pent, pes, pest, pester, pesty, pet, peter, pets, phi
philter, philtre, phiz, pht, print, pst, rah, rai, rap, raphe
raphes, reap, rear, rei, ret, rete, rets, rhaphe, rhaphes, rhea
ria, rile, riles, riley, rin, rye, ryes, seg, sel, sen
sent, senti, set, sew, spelt, spelter, spent, splent, spline, splint
split, stent, step, stey, stria, striae, sty, stye, tea, tear
teg, tegs, tel, ten, tent, thae, the, their, then, these
thesp, they, thin, thine, thir, thirl, til, tile, tiles, tilt
tilter, tilth, tilts, tin, tine, tines, tirl, trey, treys, trog
try, tye, tyer, tyes, tyre, tyro, west, wester, wry, wryest
wye, wyes, wyte, wytes, yea, yeah, year, yeh, yelp, yelps
yen, yep, yeps, yes, yester, yet, yew, yews, zero, zori
इसके लिए मैंने TWL06 टूर्नामेंट स्क्रैबल वर्ड सूची का उपयोग किया , क्योंकि मूल प्रश्न में लिंक अब काम नहीं करता है। यह फ़ाइल 1.85MB की है, इसलिए यह थोड़ी छोटी है। और buildDictionary
फ़ंक्शन 3 से कम अक्षरों वाले सभी शब्दों को बाहर निकालता है।
यहाँ इस प्रदर्शन के बारे में टिप्पणियों के एक जोड़े हैं:
यह विक्टर निकोलेट के OCaml कार्यान्वयन के कथित प्रदर्शन की तुलना में लगभग 10 गुना धीमा है। चाहे यह अलग-अलग एल्गोरिथ्म के कारण हो, वह जिस छोटे शब्दकोष का उपयोग करता है, यह तथ्य कि उसका कोड संकलित है और मेरा जावा वर्चुअल मशीन में चलता है, या हमारे कंप्यूटरों का प्रदर्शन (मेरा एक इंटेल Q6600 @ 2.4MHz WinXP चल रहा है), मुझे नहीं पता। लेकिन यह मूल प्रश्न के अंत में उद्धृत अन्य कार्यान्वयन के परिणामों से बहुत तेज है। इसलिए, यह एल्गोरिथ्म ट्राई डिक्शनरी से बेहतर है या नहीं, मैं इस बिंदु पर नहीं जानता।
तालिका विधि का उपयोग checkWordTriplets()
वास्तविक उत्तरों के लिए एक बहुत अच्छा सन्निकटन पैदा करता है। केवल 1 3-5 में शब्द पारित कर यह असफल हो जायेगी द्वारा checkWords()
परीक्षण (देखें उम्मीदवारों की संख्या बनाम वास्तविक शब्दों की संख्या से ऊपर)।
ऐसा कुछ जिसे आप ऊपर नहीं देख सकते हैं: checkWordTriplets()
फ़ंक्शन लगभग 3.65ms लेता है और इसलिए खोज प्रक्रिया में पूरी तरह से प्रभावी है। इस checkWords()
समारोह में शेष 0.05-0.20 मिसे बहुत अधिक है।
checkWordTriplets()
फ़ंक्शन का निष्पादन समय शब्दकोश आकार पर रेखीय रूप से निर्भर करता है और वस्तुतः बोर्ड आकार से स्वतंत्र है!
checkWords()
बोर्ड के आकार और शब्दों की संख्या पर निर्भर करता है जिसका निष्पादन समय निर्भर करता है checkWordTriplets()
।
checkWords()
कार्यान्वयन ऊपर बेवकूफ पहले संस्करण मैं के साथ आया है। यह मूल रूप से बिल्कुल भी अनुकूलित नहीं है। लेकिन इसकी तुलना में checkWordTriplets()
यह एप्लिकेशन के कुल प्रदर्शन के लिए अप्रासंगिक है, इसलिए मैंने इसके बारे में चिंता नहीं की। लेकिन , अगर बोर्ड का आकार बड़ा हो जाता है, तो यह फ़ंक्शन धीमा और धीमा हो जाएगा और अंततः बात करना शुरू कर देगा। फिर, इसे भी अनुकूलित करने की आवश्यकता होगी।
इस कोड के बारे में एक अच्छी बात इसकी लचीलापन है:
- आप आसानी से बोर्ड का आकार बदल सकते हैं: अपडेट लाइन 10 और स्ट्रिंग सरणी को पास किया गया
initializeBoard()
।
- यह बड़े / अलग अक्षर का समर्थन कर सकता है और 'Qu' को एक अक्षर के रूप में बिना किसी प्रदर्शन ओवरहेड के इलाज के लिए संभाल सकता है। ऐसा करने के लिए, किसी को पंक्ति 9 और उन स्थानों के जोड़े को अपडेट करना होगा जहां वर्ण संख्या में परिवर्तित हो जाते हैं (वर्तमान में ASCII मान से 65 घटाकर)
ठीक है, लेकिन मुझे लगता है कि अब तक यह पोस्ट काफी लंबी है। आपके द्वारा किए जा रहे किसी भी प्रश्न का उत्तर मैं निश्चित रूप से दे सकता हूं, लेकिन इसे टिप्पणियों में स्थानांतरित करें।