2009-11-16 18 views
15

क्या कोई मेरे लिए वर्तमान में उपयोग कर रहे एक से अधिक कुशल कार्टेसियन उत्पाद एल्गोरिदम प्रदर्शित कर सकता है (मान लीजिए कि एक है)। मैंने एसओ के चारों ओर देखा है और थोड़ा गुस्से में है लेकिन कुछ भी स्पष्ट नहीं देख सकता है इसलिए मुझे कुछ याद आ रहा है।कुशल कार्टेशियन उत्पाद एल्गोरिदम

foreach (int i in is) { 
    foreach (int j in js) { 
     //Pair i and j 
    } 
} 

यह मेरे कोड में जो कुछ करता है उसका एक बहुत ही सरल संस्करण है। दो पूर्णांक लुकअप कुंजियां हैं जिनका उपयोग एक/अधिक ऑब्जेक्ट्स को पुनर्प्राप्त करने के लिए किया जाता है और दो लुकअप से सभी ऑब्जेक्ट्स को नई ऑब्जेक्ट्स में जोड़ा जाता है।

एक बहुत बड़ी जटिल प्रणाली में कोड का यह छोटा ब्लॉक एक प्रमुख प्रदर्शन बाधा बन जाता है क्योंकि यह डेटासेट स्केल पर चल रहा है। इनमें से कुछ वस्तुओं और लुकअप को संग्रहीत करने के लिए उपयोग की जाने वाली डेटा संरचनाओं में सुधार करके कम हो सकता है लेकिन मुझे लगता है कि मुख्य मुद्दा अभी भी कार्टेशियन उत्पाद की गणना है।

संपादित

तो एल्गोरिथ्म के अपने विशिष्ट उपयोग पर कुछ अधिक पृष्ठभूमि को देखने के लिए अगर कोई चाल है कि मैं मार्क की टिप्पणी के जवाब में उपयोग कर सकते हैं हो सकता है। समग्र प्रणाली एक SPARQL क्वेरी इंजन है जो ग्राफ़ डेटा के सेट पर SPARQL क्वेरीज को प्रोसेस करता है, SPARQL एक पैटर्न आधारित भाषा है, इसलिए प्रत्येक क्वेरी में पैटर्न की एक श्रृंखला होती है जो ग्राफ़ के विरुद्ध मेल खाती है। ऐसे मामले में जहां दो अनुवर्ती पैटर्नों में कोई सामान्य चर नहीं है (वे अलग हैं) समग्र क्वेरी के संभावित समाधानों का सेट प्राप्त करने के लिए दो पैटर्न द्वारा उत्पादित समाधानों के कार्टेशियन उत्पाद की गणना करना आवश्यक है। पैटर्न की कोई संख्या हो सकती है और मुझे कई बार कार्टेशियन उत्पादों की गणना करना पड़ सकता है जो संभावित समाधानों में काफी घातीय विस्तार कर सकते हैं यदि क्वेरी विवादित पैटर्न की श्रृंखला से बना है।

मौजूदा जवाब मुझे शक है वहाँ क्या किसी भी चाल है कि लागू हो सकते हैं से किसी तरह

अद्यतन

तो मैंने सोचा कि मैं क्या मैं आदेश कार्तीय करने की ज़रूरत को कम करने में कार्यान्वित पर एक अपडेट पोस्ट करेंगे उत्पाद और इस प्रकार आम तौर पर क्वेरी इंजन को अनुकूलित करते हैं। ध्यान दें कि उत्पादों की पूरी तरह से पूरी तरह से खत्म करना हमेशा संभव नहीं होता है, लेकिन यह अनुकूलित करना लगभग हमेशा संभव है ताकि दो सेटों का आकार जुड़ना बहुत छोटा हो।

चूंकि प्रत्येक बीजीपी (मूल ग्राफ पैटर्न) जो ट्रिपल पैटर्न का एक सेट है, को ब्लॉक (संक्षेप में) के रूप में निष्पादित किया जाता है, इंजन इष्टतम प्रदर्शन के लिए बीजीपी के भीतर पैटर्न को पुन: व्यवस्थित करने के लिए स्वतंत्र है। उदाहरण के लिए निम्नलिखित BGP पर विचार करें: के रूप में क्वेरी है

?a :someProperty ?b . 
?c :anotherProperty ?d . 
?b a :Class . 

निष्पादित एक कार्तीय उत्पाद के बाद से पहली पैटर्न के परिणामों दूसरा पैटर्न से संबंध तोड़ना हैं की आवश्यकता है ताकि पहले दो पैटर्न के परिणामों के एक कार्तीय उत्पाद है उनके व्यक्तिगत परिणाम इस परिणाम में हमें वास्तव में आवश्यकता होने की तुलना में कहीं अधिक परिणाम होंगे, क्योंकि तीसरा पैटर्न पहले पैटर्न के संभावित परिणामों को प्रतिबंधित करता है लेकिन हम बाद में इस प्रतिबंध को लागू नहीं करते हैं। लेकिन हम तो जैसे को पुन: व्यवस्थित करता है, तो:

?b a :Class . 
?a :someProperty ?b . 
?c :anotherProperty ?d . 

हम अभी भी एक कार्तीय उत्पाद की आवश्यकता होगी अंतिम परिणाम प्राप्त करने के लिए 2 और 3 पैटर्न अभी भी संबंध तोड़ना हैं के बाद से, लेकिन पुन: क्रम देकर हम 2 पैटर्न के परिणामों का आकार प्रतिबंधित जिसका मतलब है कि हमारे कार्टेशियन उत्पाद का आकार बहुत छोटा होगा।

हमारे द्वारा किए गए कुछ अन्य अनुकूलन हैं लेकिन मैं उन्हें यहां पोस्ट नहीं कर रहा हूं क्योंकि यह SPARQL इंजन आंतरिकों की काफी विस्तृत चर्चा में शामिल होना शुरू कर देता है। अगर कोई अधिक जानकारी में रुचि रखता है तो बस एक टिप्पणी छोड़ या मुझे एक ट्वीट @RobVesse

+0

http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx – tugberk

उत्तर

31

कार्तीय उत्पाद की जटिलता भेज ओ (n) है, कोई शॉर्टकट नहीं है।

विशिष्ट मामलों में, आपके दो अक्ष को पुन: क्रमबद्ध करने का क्रम महत्वपूर्ण है। उदाहरण के लिए, यदि आपका कोड किसी सरणी में प्रत्येक स्लॉट पर जा रहा है - या छवि में प्रत्येक पिक्सेल - तो आपको प्राकृतिक क्रम में स्लॉट पर जाने का प्रयास करना चाहिए। एक छवि आमतौर पर 'स्कैनलाइन' में रखी जाती है, इसलिए किसी भी वाई पर पिक्सल निकट हैं। इसलिए, आपको बाहरी लूप पर वाई और आंतरिक पर एक्स पर पुन: प्रयास करना चाहिए।

चाहे आपको कार्टशियन उत्पाद की आवश्यकता हो या Wherther एक अधिक कुशल एल्गोरिदम है जिसे आप हल कर रहे हैं उस पर निर्भर करता है।

+6

+1 डेटा इलाके को प्रोत्साहित करने के लिए +1! –

+2

बिल्कुल, कार्टेशियन उत्पाद आउटपुट ओ (एन^2) है जिसका मतलब है कि स्मृति लागत ओ (एन^2) संचालन में आउटपुट लिखना, इसलिए कोई एल्गोरिदम तेजी से नहीं हो सकता है। – ldog

10

आप कुछ अतिरिक्त ज्ञान के बिना वास्तव में नेस्टेड लूप के प्रदर्शन को नहीं बदल सकते हैं, लेकिन यह उपयोग-विशिष्ट होगा। यदि is में n आइटम js में आइटम हैं, तो यह हमेशा ओ (एन * एम) होने जा रहा है।

आप इसके बारे में नज़र बदल सकते हैं:

var qry = from i in is 
      from j in js 
      select /*something involving i/j */; 

यह अभी भी O (n * मी) है, लेकिन नाममात्र अतिरिक्त LINQ की भूमि के ऊपर है (आप सामान्य में यह नोटिस नहीं होगा उपयोग, हालांकि)।

आप में अपने मामले में क्या कर रहे हैं? वहाँ चाल हो सकती है ...

एक बात को निश्चित रूप से से बचने कि मजबूर करता कुछ भी है एक क्रॉस में शामिल होने से बफ़र होना - foreach दृष्टिकोण ठीक है और बफ़र नहीं होता - लेकिन आप एक List<> लिए प्रत्येक आइटम जोड़ते हैं, फिर स्मृति प्रभाव से सावधान रहें। Ditto OrderBy आदि (अगर अनुपयुक्त उपयोग किया जाता है)।

+4

यदि आप सी # 4.0, या पीआईएलक्यू के साथ काम कर रहे हैं, और एक मल्टीकोर मशीन है, तो आप एक .ssarallel() जोड़ सकते हैं, जैसा कि 'i i में है। ASParallel() ' –

+0

ने मेरे मामले के लिए और अधिक पृष्ठभूमि जोड़ा लेकिन मुझे संदेह है LINQ दृष्टिकोण का उपयोग करके – RobV

+0

लागू करने वाली कोई भी चाल है जो मेरे मामले में उपयोगी नहीं होगी क्योंकि वास्तव में आंतरिक लूप – RobV

2

अतिरिक्त जानकारी को प्रश्न में जोड़ा गया है।

डुप्लिकेट से बचा जा सकता है यदि आप उन लोगों को रिकॉर्ड करते हैं जिन्हें आपने पहले ही गणना की है ताकि उन्हें दोबारा डुप्लिकेट करने से बचें - ऐसा माना जाता है कि ऐसी बहीखाता - एक हैशप या एक साधारण सूची - लागत कंप्यूटिंग की लागत से कम है नकल।

सी # रनटाइम वास्तव में बहुत तेज है, लेकिन चरम भारी उठाने के लिए, आप मूल कोड में छोड़ना चाहेंगे।

आप इस समस्या की आवश्यक समानता भी देख सकते हैं। यदि किसी उत्पाद की गणना किसी अन्य उत्पाद की गणना को प्रभावित नहीं करती है, तो आप समानांतर में काम करने के लिए सीधे एक से अधिक प्रोसेसर कोर का उपयोग कर सकते हैं। ThreadPool पर देखें। QueueUserWorkItem

+0

मेरे मामले में डुप्लीकेट आवश्यक हैं, कुछ प्रकार समांतरता काम कर सकती है हालांकि – RobV

1

यदि कैश इलाके (या जे को बनाए रखने के लिए आवश्यक स्थानीय मेमोरी) एक समस्या है, तो आप इनपुट एरे को दोबारा विभाजित करके अपने एल्गोरिदम को अधिक कैश-अनुकूल बना सकते हैं।कुछ ऐसा:

cartprod(is,istart,ilen, js,jstart,jlen) { 
    if(ilen <= IMIN && jlen <= JMIN) { // base case 
    for(int i in is) { 
     for(int j in js) { 
     // pair i and j 
     } 
    } 
    return; 
    } 
    if(ilen > IMIN && jlen > JMIN) { // divide in 4 
    ilen2= ilen>>1; 
    jlen2= jlen>>1; 
    cartprod(is,istart,ilen2,   js,jstart,jlen2); 
    cartprod(is,istart+ilen2,ilen-ilen2, js,jstart,jlen2); 
    cartprod(is,istart+ilen2,ilen-ilen2, js,jstart+jlen2,jlen-jlen2); 
    cartprod(is,istart,ilen2,   js,jstart+jlen2,jlen-jlen2); 
    return; 
    } 
    // handle other cases... 
} 

ध्यान दें कि यह एक्सेस पैटर्न स्वचालित कैश के सभी स्तरों का स्वचालित रूप से काफी अच्छा लाभ उठाएगा; इस तरह की तकनीक को cache-oblivious एल्गोरिदम डिज़ाइन कहा जाता है।

3

मैं ओ (एन^2) की तुलना में कुछ भी बेहतर प्रस्ताव नहीं दे सकता, क्योंकि यह आउटपुट का आकार है, और इसलिए एल्गोरिदम कोई तेज़ नहीं हो सकता है।

मैं जो सुझाव दे सकता हूं वह यह है कि आपको उत्पाद की गणना करने की आवश्यकता है या नहीं। उदाहरण के लिए, आपको उत्पाद सेट P की आवश्यकता भी नहीं हो सकती है अगर आप केवल यह पूछने जा रहे हैं कि कुछ तत्व इसके हैं। आपको केवल उस सेट के बारे में जानकारी चाहिए जो इसके द्वारा बनाई गई है।

दरअसल (स्यूडोकोड)

bool IsInSet(pair (x,y), CartesianProductSet P) 
{ 
    return IsInHash(x,P.set[1]) && IsInHash(y,P.set[2]) 
} 

कार्तीय उत्पाद इस तरह गणना की जाती है जहां:

// Cartesian product of A and B is 
P.set[1]=A; P.set[2]=B; 

आप सेट लागू करते हैं तो के रूप में हैश, तो m सेट की कार्तीय उत्पाद में देखने सिर्फ एक देखने है m हैश में आप मुफ्त में आते हैं। कार्टेशियन उत्पाद का निर्माण और IsInSet प्रत्येक O(m) समय लेते हैं, जहां mसेट्स की संख्या गुणा करता है, और यह प्रत्येक सेट के n - आकार से बहुत कम है।

+0

दुर्भाग्य से पूरे उत्पाद सेट को रखना आवश्यक है, इसलिए आपका विचार मेरे मामले में काम नहीं करेगा हालांकि यह अच्छा है – RobV

+0

@RobV, फिर, ओह, आप एन-स्क्वायर रहे हैं! :-) –

1

मुझे नहीं पता कि सी # में जावा-जैसे-इटरेटर कैसे लिखना है, लेकिन शायद आप जानते हैं और my solution from here को सी # में स्थानांतरित कर सकते हैं।

यह दिलचस्प हो सकता है यदि आपके संयोजन उन्हें पूरी तरह स्मृति में रखने के लिए बहुत बड़े हैं।

हालांकि, यदि आप संग्रह पर विशेषता द्वारा फ़िल्टर करते हैं, तो आपको संयोजन बनाने से पहले फ़िल्टर करना चाहिए। उदाहरण:

यदि आपके पास 1 से 1000 और यादृच्छिक शब्द हैं और उन्हें गठबंधन करें, और फिर उन संयोजनों को फ़िल्टर करें, जहां संख्या 20 तक विभाजित हो और शब्द 'डी' से शुरू होता है, तो आपके पास 1000 * (26 हो सकता है) * एक्स) = 26000 * एक्स संयोजन खोजने के लिए।

या आप पहले नंबरों को फ़िल्टर करते हैं, जो आपको 50 नंबर देता है, और (यदि समान रूप से वितरित किया जाता है) 1 वर्ण, जो अंत में केवल 50 * x तत्व होते हैं।

+0

हां ऐसा कुछ सैद्धांतिक रूप से संभव है हालांकि मेरे वास्तुकला में आसानी से लागू नहीं किया जाता है। जहां भी संभव हो, पहले +1 फ़िल्टरिंग करें, हम वास्तव में जहां भी संभव हो वहां उत्पाद गणना में फ़िल्टर शामिल करते हैं और हमेशा उत्पाद की गणना करने से पहले उत्पाद के केवल एक तरफ लागू होने वाले फ़िल्टर को आजमाएं और लागू करें – RobV

संबंधित मुद्दे