आज मैं एक मनमाने ढंग से गहरे ग्राफ को पार करने के लिए एक विधि को लागू करने जा रहा था और इसे एक समरूप में फ़्लैट कर रहा था। इसके बजाय, मैं एक छोटे से खोज पहले किया था और यह पाया:LINQ के साथ कुशल ग्राफ ट्रैवर्सल - रिकर्सन को हटाएं
public static IEnumerable<T> Traverse<T>(this IEnumerable<T> enumerable, Func<T, IEnumerable<T>> recursivePropertySelector)
{
foreach (T item in enumerable)
{
yield return item;
IEnumerable<T> seqRecurse = recursivePropertySelector(item);
if (seqRecurse == null) continue;
foreach (T itemRecurse in Traverse(seqRecurse, recursivePropertySelector))
{
yield return itemRecurse;
}
}
}
सिद्धांत रूप में यह अच्छा लग रहा है, लेकिन व्यवहार में मैंने पाया यह काफी बराबर हाथ से लिखा कोड का उपयोग कर (स्थिति उत्पन्न होती है के रूप में) की तुलना में अधिक खराब प्रदर्शन एक ग्राफ के माध्यम से जाने के लिए और जो कुछ भी करने की जरूरत है करने के लिए। मुझे संदेह है कि ऐसा इसलिए है क्योंकि इस विधि में, प्रत्येक आइटम के लिए यह लौटाता है, स्टैक को कुछ मनमाने ढंग से गहरे स्तर से खोलना पड़ता है।
मुझे यह भी संदेह है कि अगर रिकर्सन समाप्त हो गया है तो यह विधि बहुत अधिक कुशलता से चलती है। मैं भी रिकर्सन को खत्म करने में बहुत अच्छा नहीं होता।
क्या किसी को पता है कि रिकर्सन को खत्म करने के लिए इस विधि को फिर से लिखना है?
किसी भी मदद के लिए धन्यवाद।
संपादित करें: सभी विस्तृत प्रतिक्रियाओं के लिए बहुत बहुत धन्यवाद। मैंने एरिक के समाधान बनाम मूल समाधान बेंचमार्किंग की कोशिश की है, जो एक गणक विधि का उपयोग नहीं कर रहा है और बदले में एक लैम्ब्डा के साथ उलझन में चल रहा है और विचित्र रूप से, लैम्ब्डा रिकर्सन दो अन्य विधियों में से किसी एक की तुलना में काफी तेज़ है।
class Node
{
public List<Node> ChildNodes { get; set; }
public Node()
{
ChildNodes = new List<Node>();
}
}
class Foo
{
public static void Main(String[] args)
{
var nodes = new List<Node>();
for(int i = 0; i < 100; i++)
{
var nodeA = new Node();
nodes.Add(nodeA);
for (int j = 0; j < 100; j++)
{
var nodeB = new Node();
nodeA.ChildNodes.Add(nodeB);
for (int k = 0; k < 100; k++)
{
var nodeC = new Node();
nodeB.ChildNodes.Add(nodeC);
for(int l = 0; l < 12; l++)
{
var nodeD = new Node();
nodeC.ChildNodes.Add(nodeD);
}
}
}
}
nodes.TraverseOld(node => node.ChildNodes).ToList();
nodes.TraverseNew(node => node.ChildNodes).ToList();
var watch = Stopwatch.StartNew();
nodes.TraverseOld(node => node.ChildNodes).ToList();
watch.Stop();
var recursiveTraversalTime = watch.ElapsedMilliseconds;
watch.Restart();
nodes.TraverseNew(node => node.ChildNodes).ToList();
watch.Stop();
var noRecursionTraversalTime = watch.ElapsedMilliseconds;
Action<Node> visitNode = null;
visitNode = node =>
{
foreach (var child in node.ChildNodes)
visitNode(child);
};
watch.Restart();
foreach(var node in nodes)
visitNode(node);
watch.Stop();
var lambdaRecursionTime = watch.ElapsedMilliseconds;
}
}
कहाँ TraverseOld मूल विधि है, TraverseNew एरिक के तरीका है, और जाहिर है लैम्ब्डा लैम्ब्डा है।
मेरी मशीन पर, ट्रैवर्सऑल्ड 10127 एमएस लेता है, ट्रैवर्सन्यू 3038 एमएस लेता है, लैम्ब्डा रिकर्सन 1181 एमएस लेता है।
क्या यह सामान्य है कि गणनाकर्ता विधियां (उपज रिटर्न के साथ) तत्काल निष्पादन के विरोध में 3 एक्स तक ले सकती हैं? या यहाँ कुछ और चल रहा है?
पहली नज़र में, यह लग रहा है इस विधि की तरह गुणा recursing है प्रत्येक स्तर पर, यानी, एक बेवकूफ fibonacci 'f (x) = f (x - 1) + की तरह एफ (एक्स - 2) '। – mellamokb
अंतिम संस्करण पहले दो के समान काम के करीब कहीं भी नहीं कर रहा है; विशेष रूप से, यह किसी भी सूचियों को आवंटित नहीं कर रहा है, सरणी से सरणी में आइटम कॉपी कर रहा है, और इसी तरह। कल्पना कीजिए कि आप जनगणना लेने वाले हैं; यदि आप बस घर से घर चलने का फैसला करते हैं और परेशान नहीं करते हैं, तो आप जानते हैं, किसी भी जानकारी को लिखना, फिर आपका काम बहुत तेज हो जाएगा। –
जहां मैं ट्रैवर्सऑल्ड और ट्रैवर्सन्यू 3 साल बाद पा सकता हूं? – user1756694