যেহেতু দৈর্ঘ্যটি একটি মানদণ্ড হিসাবে তালিকাভুক্ত করা হয়েছে, এখানে 1681 টি অক্ষরে গল্ফযুক্ত সংস্করণ রয়েছে (সম্ভবত এখনও 10% দ্বারা উন্নত হতে পারে):
import java.io.*;import java.util.*;public class W{public static void main(String[]
a)throws Exception{int n=a.length<1?5:a[0].length(),p,q;String f,t,l;S w=new S();Scanner
s=new Scanner(new
File("sowpods"));while(s.hasNext()){f=s.next();if(f.length()==n)w.add(f);}if(a.length<1){String[]x=w.toArray(new
String[0]);Random
r=new Random();q=x.length;p=r.nextInt(q);q=r.nextInt(q-1);f=x[p];t=x[p>q?q:q+1];}else{f=a[0];t=a[1];}H<S>
A=new H(),B=new H(),C=new H();for(String W:w){A.put(W,new
S());for(p=0;p<n;p++){char[]c=W.toCharArray();c[p]='.';l=new
String(c);A.get(W).add(l);S z=B.get(l);if(z==null)B.put(l,z=new
S());z.add(W);}}for(String W:A.keySet()){C.put(W,w=new S());for(String
L:A.get(W))for(String b:B.get(L))if(b!=W)w.add(b);}N m,o,ñ;H<N> N=new H();N.put(f,m=new
N(f,t));N.put(t,o=new N(t,t));m.k=0;N[]H=new
N[3];H[0]=m;p=H[0].h;while(0<1){if(H[0]==null){if(H[1]==H[2])break;H[0]=H[1];H[1]=H[2];H[2]=null;p++;continue;}if(p>=o.k-1)break;m=H[0];H[0]=m.x();if(H[0]==m)H[0]=null;for(String
v:C.get(m.s)){ñ=N.get(v);if(ñ==null)N.put(v,ñ=new N(v,t));if(m.k+1<ñ.k){if(ñ.k<ñ.I){q=ñ.k+ñ.h-p;N
Ñ=ñ.x();if(H[q]==ñ)H[q]=Ñ==ñ?null:Ñ;}ñ.b=m;ñ.k=m.k+1;q=ñ.k+ñ.h-p;if(H[q]==null)H[q]=ñ;else{ñ.n=H[q];ñ.p=ñ.n.p;ñ.n.p=ñ.p.n=ñ;}}}}if(o.b==null)System.out.println(f+"\n"+t+"\nOY");else{String[]P=new
String[o.k+2];P[o.k+1]=o.k-1+"";m=o;for(q=m.k;q>=0;q--){P[q]=m.s;m=m.b;}for(String
W:P)System.out.println(W);}}}class N{String s;int k,h,I=(1<<30)-1;N b,p,n;N(String S,String
d){s=S;for(k=0;k<d.length();k++)if(d.charAt(k)!=S.charAt(k))h++;k=I;p=n=this;}N
x(){N r=n;n.p=p;p.n=n;n=p=this;return r;}}class S extends HashSet<String>{}class H<V>extends
HashMap<String,V>{}
অবহেলিত সংস্করণ, যা প্যাকেজের নাম এবং পদ্ধতি ব্যবহার করে এবং সতর্কতা দেয় না বা ক্লাসগুলি কেবলমাত্র উপনামের জন্য প্রসারিত করে না:
package com.akshor.pjt33;
import java.io.*;
import java.util.*;
// WordLadder partially golfed and with reduced dependencies
//
// Variables used in complexity analysis:
// n is the word length
// V is the number of words (vertex count of the graph)
// E is the number of edges
// hash is the cost of a hash insert / lookup - I will assume it's constant, but without completely brushing it under the carpet
public class WordLadder2
{
private Map<String, Set<String>> wordsToWords = new HashMap<String, Set<String>>();
// Initialisation cost: O(V * n * (n + hash) + E * hash)
private WordLadder2(Set<String> words)
{
Map<String, Set<String>> wordsToLinks = new HashMap<String, Set<String>>();
Map<String, Set<String>> linksToWords = new HashMap<String, Set<String>>();
// Cost: O(Vn * (n + hash))
for (String word : words)
{
// Cost: O(n*(n + hash))
for (int i = 0; i < word.length(); i++)
{
// Cost: O(n + hash)
char[] ch = word.toCharArray();
ch[i] = '.';
String link = new String(ch).intern();
add(wordsToLinks, word, link);
add(linksToWords, link, word);
}
}
// Cost: O(V * n * hash + E * hash)
for (Map.Entry<String, Set<String>> from : wordsToLinks.entrySet()) {
String src = from.getKey();
wordsToWords.put(src, new HashSet<String>());
for (String link : from.getValue()) {
Set<String> to = linksToWords.get(link);
for (String snk : to) {
// Note: equality test is safe here. Cost is O(hash)
if (snk != src) add(wordsToWords, src, snk);
}
}
}
}
public static void main(String[] args) throws IOException
{
// Cost: O(filelength + num_words * hash)
Map<Integer, Set<String>> wordsByLength = new HashMap<Integer, Set<String>>();
BufferedReader br = new BufferedReader(new FileReader("sowpods"), 8192);
String line;
while ((line = br.readLine()) != null) add(wordsByLength, line.length(), line);
if (args.length == 2) {
String from = args[0].toUpperCase();
String to = args[1].toUpperCase();
new WordLadder2(wordsByLength.get(from.length())).findPath(from, to);
}
else {
// 5-letter words are the most interesting.
String[] _5 = wordsByLength.get(5).toArray(new String[0]);
Random rnd = new Random();
int f = rnd.nextInt(_5.length), g = rnd.nextInt(_5.length - 1);
if (g >= f) g++;
new WordLadder2(wordsByLength.get(5)).findPath(_5[f], _5[g]);
}
}
// O(E * hash)
private void findPath(String start, String dest) {
Node startNode = new Node(start, dest);
startNode.cost = 0; startNode.backpointer = startNode;
Node endNode = new Node(dest, dest);
// Node lookup
Map<String, Node> nodes = new HashMap<String, Node>();
nodes.put(start, startNode);
nodes.put(dest, endNode);
// Heap
Node[] heap = new Node[3];
heap[0] = startNode;
int base = heap[0].heuristic;
// O(E * hash)
while (true) {
if (heap[0] == null) {
if (heap[1] == heap[2]) break;
heap[0] = heap[1]; heap[1] = heap[2]; heap[2] = null; base++;
continue;
}
// If the lowest cost isn't at least 1 less than the current cost for the destination,
// it can't improve the best path to the destination.
if (base >= endNode.cost - 1) break;
// Get the cheapest node from the heap.
Node v0 = heap[0];
heap[0] = v0.remove();
if (heap[0] == v0) heap[0] = null;
// Relax the edges from v0.
int g_v0 = v0.cost;
// O(hash * #neighbours)
for (String v1Str : wordsToWords.get(v0.key))
{
Node v1 = nodes.get(v1Str);
if (v1 == null) {
v1 = new Node(v1Str, dest);
nodes.put(v1Str, v1);
}
// If it's an improvement, use it.
if (g_v0 + 1 < v1.cost)
{
// Update the heap.
if (v1.cost < Node.INFINITY)
{
int bucket = v1.cost + v1.heuristic - base;
Node t = v1.remove();
if (heap[bucket] == v1) heap[bucket] = t == v1 ? null : t;
}
// Next update the backpointer and the costs map.
v1.backpointer = v0;
v1.cost = g_v0 + 1;
int bucket = v1.cost + v1.heuristic - base;
if (heap[bucket] == null) {
heap[bucket] = v1;
}
else {
v1.next = heap[bucket];
v1.prev = v1.next.prev;
v1.next.prev = v1.prev.next = v1;
}
}
}
}
if (endNode.backpointer == null) {
System.out.println(start);
System.out.println(dest);
System.out.println("OY");
}
else {
String[] path = new String[endNode.cost + 1];
Node t = endNode;
for (int i = t.cost; i >= 0; i--) {
path[i] = t.key;
t = t.backpointer;
}
for (String str : path) System.out.println(str);
System.out.println(path.length - 2);
}
}
private static <K, V> void add(Map<K, Set<V>> map, K key, V value) {
Set<V> vals = map.get(key);
if (vals == null) map.put(key, vals = new HashSet<V>());
vals.add(value);
}
private static class Node
{
public static int INFINITY = Integer.MAX_VALUE >> 1;
public String key;
public int cost;
public int heuristic;
public Node backpointer;
public Node prev = this;
public Node next = this;
public Node(String key, String dest) {
this.key = key;
cost = INFINITY;
for (int i = 0; i < dest.length(); i++) if (dest.charAt(i) != key.charAt(i)) heuristic++;
}
public Node remove() {
Node rv = next;
next.prev = prev;
prev.next = next;
next = prev = this;
return rv;
}
}
}
আপনি দেখতে পাচ্ছেন, চলমান ব্যয়ের বিশ্লেষণটি হ'ল O(filelength + num_words * hash + V * n * (n + hash) + E * hash)
। আপনি যদি আমার অনুমানটি গ্রহণ করেন যে একটি হ্যাশ টেবিল সন্নিবেশ / লুক করা ধ্রুব সময়, এটাই O(filelength + V n^2 + E)
। SOWPODS- র গ্রাফগুলির নির্দিষ্ট পরিসংখ্যানগুলির অর্থ হ'ল O(V n^2)
সত্যই O(E)
বেশিরভাগের জন্য আধিপত্য n
।
নমুনা আউটপুট:
IDOLA, IDOLS, IDYLS, ODYLS, ODALS, OVALS, OVELS, OVENS, ইভেনস, ETENS, স্টেনস, স্কিনস, স্কিনস, স্পিনস, স্পিন, 13
উইককা, প্রস, ওআই
ব্রিন, ব্রিনস, ট্রিনস, টেইনস, টিআরএনএস, ইয়ার্নস, ইয়াউএনএস, ইএইডাব্লুএস, ইয়াপিএস, 7
গ্যালস, গ্যাস, গাস্টস, জিইএসটিএস, জিএসটিই, জিইএসএসই, ডিএসসি, ৫
সার, ডিওরিজ, ডান, ডাইনস, ডিংস, ডিংজি, ৪
LICHT, আলো, হালকা, বড়, বিগস, বায়রোস, জিরস, গিরস, গার্নস, গুয়ানস, গুয়ানা, রুয়ানা, ১০
সারগ, সার্জ, সেরি, সেরস, সেরস, ডিয়ার্স, ডিয়ারস, ওয়ার্স, ওভারস, ওভেলস, ওভালস, ওডালস, ওডিআইএলএস, আইডিআইএলএস, 12
কেরস, সায়ারস, সেরস, বিয়ারস, ব্রেরস, ব্রির, ফ্রিম, ক্রিম, ক্রাইপ, 7
সবচেয়ে দীর্ঘতম পথের সাথে এটি 6 টির মধ্যে একটি জুটি:
গেইনস্ট, ফেইনস্ট, ফেয়ারেস্ট, স্যারেস্ট, সাইয়েস্ট, স্যাডেস্ট, ম্যাসেস্ট, মিডলিস্ট, মিলিস্টেস্ট, উইলডিস্ট, উইলিয়ামেস্ট, ওয়ালিস্ট, ওয়ানিসেস্ট, ক্যানিয়েস্ট, ক্যান্টেস্ট, কনটেক্সট, কনসার্ট, কনসার্ট, কনসপ্সারস পপপিটস, পোপস, পোপস, মোসিজি, মোসিস, মাউস, পাউস, প্লাসস, প্লাইসস, প্রাইসস, প্রেসস, প্রাইসস, ইউরিয়াডস, ইউনিসেস, ইউনিসেস, ইউনিসেড, ইউনডেড, ইউনডেড, ইউনডেড, ইউনডেড সূচিপত্র, সূচকগুলি, সূচিপত্রগুলি, ইনসেন্টস, ইনসেটস, ইনফেষ্টস, ইনফ্যাক্টস, ইনজেক্টস, 56
এবং সবচেয়ে খারাপ ক্ষেত্রে দ্রবণীয় 8-অক্ষরের জোড়া:
এনক্রবিং, আন্রোবাইং, আনরোপিং, আনকপিং, আনক্যাপিং, এনক্র্যাগিং, এনক্র্যাগিং, এনক্র্যাগিং, এনক্রাইজিং, এনল্যাকিং, উন্মুক্ত, উদ্বোধন, খেলা, স্ট্রাইকিং, স্ট্রোপিং, স্ট্রোপিং, স্ট্রোপিং, স্ট্রোপিং, স্ট্রোপিং, স্ট্রোকিং ক্রিম্পিং, ক্রিস্পিং, ক্রিস্পিনস, ক্রাইস্পেনস, ক্রাইস্পারস, ক্রিম্পারস, ক্র্যাম্পারস, ক্ল্যাপার্স, ক্ল্যাপার্স, ক্ল্যাশারস, স্ল্যাশার্স, স্ল্যাথার্স, স্লাইথার্স, স্মিথারস, স্যোথার্স, দক্ষিণী, মুখোচী, মুখোচী, মুখোমুখি মধ্যাহ্নভোজন, লাইঙ্কার্স, লিঙ্কস, লাইনচেটস, ৫২
এখন যেহেতু আমি মনে করি যে আমার প্রশ্নের সমস্ত প্রয়োজনীয়তা আমার আলোচনার বাইরে চলে গেছে।
একটি কমপিএসসিআইয়ের জন্য প্রশ্নটি স্পষ্টতই একটি গ্রাফ জি এর সংক্ষিপ্ততম পথকে হ্রাস করবে যার প্রান্তিক চিহ্নগুলি এবং যার প্রান্তগুলি একটি বর্ণকে পৃথক করে শব্দগুলিকে সংযুক্ত করে। দক্ষতার সাথে গ্রাফ তৈরি করা তুচ্ছ নয় - ও (ভি এন হ্যাশ + ই) এর জটিলতা হ্রাস করতে আমার পুনরায় ঘুরে দেখার দরকারের একটি ধারণা আমার আছে। আমি যেভাবে এটি করি তার মধ্যে এমন একটি গ্রাফ তৈরি করা জড়িত যা অতিরিক্ত শিখুন সন্নিবেশ করায় (একটি ওয়াইল্ডকার্ড অক্ষরের সাথে শব্দের সাথে মিল রেখে) এবং প্রশ্নের মধ্যে থাকা গ্রাফের সাথে হোমোমোরফিক। আমি জি-তে হ্রাস করার পরিবর্তে সেই গ্রাফটি ব্যবহার করার বিষয়টি বিবেচনা করেছি - এবং আমি মনে করি যে গল্ফিং দৃষ্টিকোণ থেকে আমার করা উচিত ছিল - সেই ভিত্তিতে যে 3 টিরও বেশি প্রান্তযুক্ত একটি ওয়াইল্ডকার্ড নোড গ্রাফের প্রান্তকে কমিয়ে দেয় এবং সংক্ষিপ্ত পাথ অ্যালগরিদমের স্ট্যান্ডার্ড সবচেয়ে খারাপ ক্ষেত্রে চলমান সময় O(V heap-op + E)
।
যাইহোক, আমি প্রথমটি করলাম হ'ল গ্রাফের কয়েকটি শব্দের দৈর্ঘ্যের জন্য গ্রাফের কিছু বিশ্লেষণ চালানো এবং আমি আবিষ্কার করেছি যে তারা 5 বা ততোধিক অক্ষরের শব্দের জন্য অত্যন্ত বিরল। 5-অক্ষরের গ্রাফটিতে 12478 শীর্ষ এবং 40759 প্রান্ত রয়েছে; লিঙ্ক নোড যুক্ত করা গ্রাফটিকে আরও খারাপ করে। আপনি 8 টি অক্ষরের উপরে নোডের চেয়ে কম কিনারা পাবেন এবং 3/7 শব্দের "এলফ" রয়েছে। সুতরাং আমি সেই অপ্টিমাইজেশান ধারণাটিকে সত্যই সহায়ক নয় বলে প্রত্যাখ্যান করেছি।
যে ধারণাটি সহায়ক প্রমাণিত হয়েছিল তা হ'ল গাদা পরীক্ষা করা। আমি সত্যই বলতে পারি যে আমি অতীতে কিছু পরিমিতরূপে বহিরাগত হিপ বাস্তবায়ন করেছি, তবে এর মতো বিদেশী আর কেউ নেই। আমি এ-তারা ব্যবহার করি (যেহেতু সি আমি ব্যবহার করছি সেই ढीগের সাহায্যে কোনও উপকার সরবরাহ করে না) লক্ষ্য থেকে পৃথক বর্ণের সংখ্যার সুস্পষ্ট হিউরিস্টিক এবং কিছুটা বিশ্লেষণ দেখায় যে যে কোনও সময়ে 3 টিরও বেশি অগ্রাধিকার নেই গাদা। আমি যখন কোনও নোড পপ করি যার অগ্রাধিকার (ব্যয় + হিউরিস্টিক) হয় এবং এর প্রতিবেশীদের দিকে তাকাই, তখন তিনটি ক্ষেত্রে আমি বিবেচনা করছি: 1) প্রতিবেশীর ব্যয় +1; প্রতিবেশীর হিউরিস্টিক হিউরিস্টিক -১ (কারণ যে বর্ণটি এটি পরিবর্তিত হয় এটি "সঠিক" হয়); 2) ব্যয় + 1 এবং তাত্ত্বিক + 0 (কারণ যে বর্ণটি এটি পরিবর্তিত হয় এটি "ভুল" থেকে "এখনও ভুল" তে যায়; 3) ব্যয় +1 এবং তাত্ত্বিক + 1 (কারণ যে বর্ণটি এটি পরিবর্তিত হয় এটি "সঠিক" থেকে "ভুল" হয়ে যায়)। সুতরাং আমি যদি প্রতিবেশীকে শিথিল করি তবে আমি এটিকে একই অগ্রাধিকার, অগ্রাধিকার + 1, বা অগ্রাধিকার + 2 এ সন্নিবেশ করতে যাচ্ছি। ফলস্বরূপ আমি গাদা জন্য লিঙ্কযুক্ত তালিকার একটি 3-উপাদান অ্যারের ব্যবহার করতে পারি।
আমার অনুমান সম্পর্কে আমার একটি নোট যুক্ত করা উচিত যা হ্যাশ লুকআপগুলি ধ্রুবক। খুব ভাল, আপনি বলতে পারেন, কিন্তু হ্যাশ কম্পিউটেশন সম্পর্কে কি? উত্তর যে আমি তাদের দূরে amortising করছি হল: java.lang.String
তার ক্যাশে hashCode()
, তাই মোট সময় অতিবাহিত কম্পিউটিং হ্যাশ হয়O(V n^2)
(গ্রাফ উৎপাদিত মধ্যে)।
আরও একটি পরিবর্তন রয়েছে যা জটিলতাকে প্রভাবিত করে, তবে এটি একটি অপ্টিমাইজেশান কিনা তা নিয়ে প্রশ্নটি পরিসংখ্যান সম্পর্কে আপনার অনুমানের উপর নির্ভর করে। (আইএমও "সেরা বিগ ও সলিউশন" কে একটি মানদণ্ড হিসাবে স্থাপন করা একটি ভুল কারণ একটি সাধারণ কারণ হিসাবে একটি সর্বোত্তম জটিলতা নেই: একক ভেরিয়েবল নেই)। এই পরিবর্তনটি গ্রাফ উত্পন্ন পদক্ষেপকে প্রভাবিত করে। উপরের কোডে এটি:
Map<String, Set<String>> wordsToLinks = new HashMap<String, Set<String>>();
Map<String, Set<String>> linksToWords = new HashMap<String, Set<String>>();
// Cost: O(Vn * (n + hash))
for (String word : words)
{
// Cost: O(n*(n + hash))
for (int i = 0; i < word.length(); i++)
{
// Cost: O(n + hash)
char[] ch = word.toCharArray();
ch[i] = '.';
String link = new String(ch).intern();
add(wordsToLinks, word, link);
add(linksToWords, link, word);
}
}
// Cost: O(V * n * hash + E * hash)
for (Map.Entry<String, Set<String>> from : wordsToLinks.entrySet()) {
String src = from.getKey();
wordsToWords.put(src, new HashSet<String>());
for (String link : from.getValue()) {
Set<String> to = linksToWords.get(link);
for (String snk : to) {
// Note: equality test is safe here. Cost is O(hash)
if (snk != src) add(wordsToWords, src, snk);
}
}
}
এটাই O(V * n * (n + hash) + E * hash)
। তবে O(V * n^2)
অংশটি প্রতিটি লিঙ্কের জন্য একটি নতুন এন-চরিত্রের স্ট্রিং তৈরি করে এবং তার হ্যাশকোডটি গণনা করে আসে। এটি কোনও সহায়ক শ্রেণীর সাহায্যে এড়ানো যায়:
private static class Link
{
private String str;
private int hash;
private int missingIdx;
public Link(String str, int hash, int missingIdx) {
this.str = str;
this.hash = hash;
this.missingIdx = missingIdx;
}
@Override
public int hashCode() { return hash; }
@Override
public boolean equals(Object obj) {
Link l = (Link)obj; // Unsafe, but I know the contexts where I'm using this class...
if (this == l) return true; // Essential
if (hash != l.hash || missingIdx != l.missingIdx) return false;
for (int i = 0; i < str.length(); i++) {
if (i != missingIdx && str.charAt(i) != l.str.charAt(i)) return false;
}
return true;
}
}
তারপরে গ্রাফ জেনারেশনের প্রথমার্ধ হয়ে যায়
Map<String, Set<Link>> wordsToLinks = new HashMap<String, Set<Link>>();
Map<Link, Set<String>> linksToWords = new HashMap<Link, Set<String>>();
// Cost: O(V * n * hash)
for (String word : words)
{
// apidoc: The hash code for a String object is computed as
// s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
// Cost: O(n * hash)
int hashCode = word.hashCode();
int pow = 1;
for (int j = word.length() - 1; j >= 0; j--) {
Link link = new Link(word, hashCode - word.charAt(j) * pow, j);
add(wordsToLinks, word, link);
add(linksToWords, link, word);
pow *= 31;
}
}
হ্যাশকোডের কাঠামো ব্যবহার করে আমরা লিঙ্কগুলি জেনারেট করতে পারি O(V * n)
। যাইহোক, এটি একটি নকআউট প্রভাব আছে। আমার অনুমানের অন্তর্নিহিত যে হ্যাশ লুকগুলি ধ্রুবক সময় এটি একটি ধারণা যে সমতার জন্য সামগ্রীর তুলনা করা সস্তা। তবে লিংকের সমতা পরীক্ষা O(n)
সবচেয়ে খারাপ অবস্থায় রয়েছে। সবচেয়ে খারাপ অবস্থাটি যখন আমাদের বিভিন্ন শব্দ থেকে উত্পন্ন দুটি সমতুল্য লিঙ্কগুলির মধ্যে হ্যাশ সংঘর্ষ হয় - অর্থাত্ এটি O(E)
গ্রাফ জেনারেশনের দ্বিতীয়ার্ধে বারবার ঘটে । এটি ব্যতীত, অ-সমতুল্য লিঙ্কগুলির মধ্যে হ্যাশের সংঘর্ষের সম্ভাব্য ঘটনা ব্যতীত, আমরা ভাল। সুতরাং আমরা O(V * n^2)
জন্য লেনদেন করেছি O(E * n * hash)
। পরিসংখ্যান সম্পর্কে আমার পূর্ববর্তী পয়েন্ট দেখুন।
HOUSE
আসাGORGE
2 হিসাবে রিপোর্ট করা হয়েছে I আমি বুঝতে পেরেছি যে সেখানে 2 টি মধ্যবর্তী শব্দ রয়েছে, সুতরাং এটি বোঝা যায় না, তবে # অপারেশনগুলি আরও স্বজ্ঞাত হবে।