यह समस्या जावास्क्रिप्ट के लिए एक प्रसिद्ध / "क्लासिक" अनुकूलन मुद्दा है, इस तथ्य के कारण कि जावास्क्रिप्ट तार "अपरिवर्तनीय" हैं और इसके अलावा एक स्ट्रिंग के लिए एक भी वर्ण के संघनन के लिए स्मृति आवंटन सहित और प्रतिलिपि बनाने के लिए आवश्यक है। , एक पूरी नई स्ट्रिंग।
दुर्भाग्य से, इस पृष्ठ पर स्वीकृत उत्तर गलत है, जहां "गलत" का अर्थ है साधारण एक-वर्ण स्ट्रिंग्स के लिए 3x का एक प्रदर्शन कारक, और छोटे तारों के लिए 8x-97x अधिक बार दोहराया गया, दोहराए जाने वाले वाक्यों के लिए 300x, और असीम रूप से। एल्गोरिदम की जटिलता के अनुपात की सीमा n
को अनंत तक ले जाना। साथ ही, इस पृष्ठ पर एक और उत्तर है जो लगभग सही है (पिछले 13 वर्षों में इंटरनेट पर घूमने वाले सही समाधान की कई पीढ़ियों और विविधताओं में से एक पर आधारित है)। हालांकि, यह "लगभग सही" समाधान सही एल्गोरिथ्म के एक महत्वपूर्ण बिंदु को याद करता है, जिससे 50% प्रदर्शन में गिरावट होती है।
जेएस प्रदर्शन परिणाम स्वीकार किए गए उत्तर के लिए, शीर्ष-प्रदर्शन वाले अन्य उत्तर (इस उत्तर में मूल एल्गोरिथ्म के अपमानित संस्करण के आधार पर), और यह उत्तर मेरे एल्गोरिथ्म का उपयोग करके 13 साल पहले बनाया गया था
~ अक्टूबर 2000 मैंने इस सटीक समस्या के लिए एक एल्गोरिथ्म प्रकाशित किया था जिसे व्यापक रूप से अनुकूलित, संशोधित किया गया था, फिर अंततः खराब समझा और भुला दिया गया। इस समस्या को दूर करने के लिए, अगस्त 2008 में, मैंने एक लेख प्रकाशित किया था http://www.webreference.com/programming/javascript/jkm3/3.html एल्गोरिथम को समझाते हुए और इसे सामान्य-प्रयोजन जावास्क्रिप्ट अनुकूलन के सरल के उदाहरण के रूप में उपयोग करते हुए। अब तक, वेब संदर्भ ने इस लेख से मेरी संपर्क जानकारी और यहां तक कि मेरा नाम भी साफ़ कर दिया है। और एक बार फिर, एल्गोरिथ्म को व्यापक रूप से अनुकूलित किया गया है, संशोधित किया गया है, फिर खराब रूप से समझा गया है और काफी हद तक भुला दिया गया है।
जोसेफ मायर्स द्वारा मूल स्ट्रिंग पुनरावृत्ति / गुणन जावास्क्रिप्ट एल्गोरिथ्म, पाठ Y.Ks के रूप में एक पाठ गुणन फ़ंक्शन के रूप में Y2K; वेब संदर्भ द्वारा इस रूप में अगस्त, 2008 को प्रकाशित:
http://www.webreference.com/programming/javascript/jkm3/3.html (लेख में जावास्क्रिप्ट अनुकूलन के उदाहरण के रूप में फ़ंक्शन का उपयोग किया गया है, जो केवल अजीब के लिए है नाम "stringFill3।")
/*
* Usage: stringFill3("abc", 2) == "abcabc"
*/
function stringFill3(x, n) {
var s = '';
for (;;) {
if (n & 1) s += x;
n >>= 1;
if (n) x += x;
else break;
}
return s;
}
उस लेख के प्रकाशन के दो महीने के भीतर, यह एक ही सवाल स्टैक ओवरफ्लो में पोस्ट किया गया था और अब तक मेरे रडार के नीचे उड़ गया था, जब जाहिर तौर पर इस समस्या के लिए मूल एल्गोरिथ्म को एक बार फिर से भुला दिया गया था। इस स्टैक ओवरफ्लो पेज पर उपलब्ध सबसे अच्छा समाधान मेरे समाधान का एक संशोधित संस्करण है, संभवतः कई पीढ़ियों द्वारा अलग किया गया है। दुर्भाग्य से, संशोधनों ने समाधान की इष्टतमता को बर्बाद कर दिया। वास्तव में, मेरे मूल से लूप की संरचना को बदलकर, संशोधित समाधान घातीय डुप्लिकेटिंग का एक पूरी तरह से अनावश्यक अतिरिक्त चरण निष्पादित करता है (इस प्रकार एक अतिरिक्त समय के साथ उचित उत्तर में उपयोग किए जाने वाले सबसे बड़े स्ट्रिंग में शामिल होता है और फिर इसे त्याग देता है)।
नीचे इस समस्या के सभी उत्तर और सभी के लाभ से संबंधित कुछ जावास्क्रिप्ट अनुकूलन की चर्चा शामिल है।
तकनीक: वस्तुओं या वस्तु गुणों के संदर्भ से बचें
यह समझने के लिए कि यह तकनीक कैसे काम करती है, हम एक वास्तविक जीवन जावास्क्रिप्ट फ़ंक्शन का उपयोग करते हैं जो कि जिस भी लंबाई की आवश्यकता होती है उसके तार बनाता है। और जैसा कि हम देखेंगे, अधिक अनुकूलन जोड़े जा सकते हैं!
यहां उपयोग किया जाने वाला एक फ़ंक्शन पैसे के प्रारूपण के लिए, या सीमा तक ब्लॉक डेटा को भरने के लिए पाठ के संरेखित कॉलम के लिए पैडिंग बनाना है। एक पाठ पीढ़ी फ़ंक्शन पाठ पर संचालित किसी भी अन्य फ़ंक्शन के परीक्षण के लिए चर लंबाई इनपुट की अनुमति देता है। यह फ़ंक्शन जावास्क्रिप्ट टेक्स्ट प्रोसेसिंग मॉड्यूल के महत्वपूर्ण घटकों में से एक है।
जैसा कि हम आगे बढ़ते हैं, हम स्ट्रिंग्स बनाने के लिए एक अनुकूलित एल्गोरिथ्म में मूल कोड को विकसित करते हुए दो सबसे महत्वपूर्ण अनुकूलन तकनीकों को कवर करेंगे। अंतिम परिणाम एक औद्योगिक-शक्ति, उच्च-प्रदर्शन फ़ंक्शन है जिसका मैंने हर जगह उपयोग किया है - आइटम की कीमतें और जावास्क्रिप्ट को ऑर्डर ऑर्डर, डेटा स्वरूपण और ईमेल / पाठ संदेश स्वरूपण और कई अन्य उपयोगों में संरेखित करना।
तार बनाने के लिए मूल कोड stringFill1()
function stringFill1(x, n) {
var s = '';
while (s.length < n) s += x;
return s;
}
/* Example of output: stringFill1('x', 3) == 'xxx' */
वाक्य रचना यहाँ स्पष्ट है। जैसा कि आप देख सकते हैं, हमने अधिक अनुकूलन के लिए जाने से पहले पहले ही स्थानीय फ़ंक्शन चर का उपयोग किया है।
ध्यान रखें कि एक वस्तु संपत्ति का एक निर्दोष संदर्भ है s.length
कोड में उसके प्रदर्शन को नुकसान पहुंचाता है। इससे भी बदतर, इस ऑब्जेक्ट प्रॉपर्टी के उपयोग से प्रोग्राम की सरलता को यह अनुमान लगाकर कम कर दिया जाता है कि पाठक जावास्क्रिप्ट स्ट्रिंग ऑब्जेक्ट्स के गुणों के बारे में जानता है।
इस ऑब्जेक्ट प्रॉपर्टी के उपयोग से कंप्यूटर प्रोग्राम की व्यापकता नष्ट हो जाती है। कार्यक्रम मानता है कि x
लंबाई की एक स्ट्रिंग होनी चाहिए। यह stringFill1()
एकल वर्णों की पुनरावृत्ति को छोड़कर फ़ंक्शन के अनुप्रयोग को कुछ भी सीमित करता है । यहां तक कि एकल वर्णों का उपयोग नहीं किया जा सकता है यदि उनमें HTML इकाई की तरह कई बाइट्स हों
।
किसी ऑब्जेक्ट प्रॉपर्टी के इस अनावश्यक उपयोग के कारण सबसे खराब समस्या यह है कि फ़ंक्शन एक अनन्त लूप बनाता है यदि रिक्त इनपुट स्ट्रिंग पर परीक्षण किया जाता है x
। सामान्यता की जांच करने के लिए, छोटी से छोटी संभव इनपुट के लिए एक कार्यक्रम लागू करें। एक प्रोग्राम जो उपलब्ध मेमोरी की मात्रा से अधिक होने पर क्रैश करता है, उसका एक बहाना है। इस तरह का एक कार्यक्रम जो कुछ भी नहीं बनाने के लिए कहा जाता है, वह अस्वीकार्य है। कभी-कभी सुंदर कोड जहरीला कोड होता है।
सादगी कंप्यूटर प्रोग्रामिंग का एक अस्पष्ट लक्ष्य हो सकता है, लेकिन आम तौर पर यह नहीं है। जब किसी कार्यक्रम में सामान्यता का कोई उचित स्तर नहीं होता है, तो यह कहना मान्य नहीं है, "यह कार्यक्रम जितना अच्छा है उतना ही अच्छा है।" जैसा कि आप देख सकते हैं, string.length
संपत्ति का उपयोग इस कार्यक्रम को सामान्य सेटिंग में काम करने से रोकता है, और वास्तव में, गलत प्रोग्राम ब्राउज़र या सिस्टम क्रैश का कारण बनने के लिए तैयार है।
क्या इस जावास्क्रिप्ट के प्रदर्शन को सुधारने के साथ-साथ इन दो गंभीर समस्याओं का भी ध्यान रखना है?
बेशक। बस पूर्णांक का उपयोग करें।
तार बनाने के लिए अनुकूलित कोड stringFill2()
function stringFill2(x, n) {
var s = '';
while (n-- > 0) s += x;
return s;
}
तुलना करने के लिए समय कोड stringFill1()
औरstringFill2()
function testFill(functionToBeTested, outputSize) {
var i = 0, t0 = new Date();
do {
functionToBeTested('x', outputSize);
t = new Date() - t0;
i++;
} while (t < 2000);
return t/i/1000;
}
seconds1 = testFill(stringFill1, 100);
seconds2 = testFill(stringFill2, 100);
अब तक की सफलता stringFill2()
stringFill1()
100-बाइट स्ट्रिंग को भरने के लिए 47.297 माइक्रोसेकंड (सेकंड का मिलियन) stringFill2()
लेता है , और यही काम करने के लिए 27.68 माइक्रोसेकंड लेता है। यह एक वस्तु संपत्ति के संदर्भ से बचकर प्रदर्शन में लगभग दोगुना है।
तकनीक: छोटे तार को लंबे तारों से जोड़ने से बचें
हमारा पिछला परिणाम अच्छा लगा - बहुत अच्छा, वास्तव में। stringFill2()
हमारे पहले दो अनुकूलन के उपयोग के कारण बेहतर कार्य बहुत तेजी से होता है। क्या आप इस पर विश्वास करेंगे अगर मैंने आपसे कहा कि यह अब की तुलना में कई गुना तेज हो सकता है?
हां, हम उस लक्ष्य को पूरा कर सकते हैं। अभी हमें यह समझाने की आवश्यकता है कि हम छोटे तारों को लंबे तारों से जोड़ने से कैसे बचें।
हमारे मूल कार्य की तुलना में अल्पकालिक व्यवहार काफी अच्छा प्रतीत होता है। कंप्यूटर वैज्ञानिक किसी फ़ंक्शन या कंप्यूटर प्रोग्राम एल्गोरिथ्म के "असममित व्यवहार" का विश्लेषण करना पसंद करते हैं, जिसका अर्थ है कि बड़े इनपुट के साथ परीक्षण करके इसके दीर्घकालिक व्यवहार का अध्ययन करना। कभी-कभी और परीक्षण किए बिना, कभी भी उन तरीकों के बारे में पता नहीं चलता है जिनसे कंप्यूटर प्रोग्राम को बेहतर बनाया जा सकता है। क्या होगा देखने के लिए, हम एक 200-बाइट स्ट्रिंग बनाने जा रहे हैं।
जो समस्या दिखती है stringFill2()
हमारे टाइमिंग फ़ंक्शन का उपयोग करते हुए, हम पाते हैं कि 200-बाइट स्ट्रिंग के लिए समय 62.54 माइक्रोसेकंड तक बढ़ जाता है, जबकि 100-बाइट स्ट्रिंग के लिए 27.68 है। ऐसा लगता है कि समय को दोगुना करने के लिए दोगुना काम करना चाहिए, लेकिन इसके बजाय यह तिगुना या चौगुना है। प्रोग्रामिंग अनुभव से, यह परिणाम अजीब लगता है, क्योंकि यदि कुछ भी हो, तो फ़ंक्शन को थोड़ा तेज़ी से होना चाहिए क्योंकि कार्य अधिक कुशलता से किया जा रहा है (फ़ंक्शन फ़ंक्शन प्रति कॉल 200 बाइट्स के बजाय 200 बाइट्स)। इस मुद्दे को जावास्क्रिप्ट स्ट्रिंग्स की एक कपटी संपत्ति के साथ करना है: जावास्क्रिप्ट स्ट्रिंग्स "अपरिवर्तनीय हैं।"
अपरिवर्तनीय का अर्थ है कि आपके द्वारा बनाया जाने के बाद आप एक स्ट्रिंग नहीं बदल सकते। एक बार में एक बाइट को जोड़कर, हम प्रयास के एक और बाइट का उपयोग नहीं कर रहे हैं। हम वास्तव में पूरे स्ट्रिंग प्लस एक और बाइट को फिर से बना रहे हैं।
वास्तव में, 100 बाइट स्ट्रिंग में एक और बाइट जोड़ने के लिए, यह 101 बाइट्स के लायक काम करता है। आइए N
बाइट्स की एक स्ट्रिंग बनाने के लिए कम्प्यूटेशनल लागत का संक्षेप में विश्लेषण करें । पहली बाइट को जोड़ने की लागत कम्प्यूटेशनल प्रयास की 1 इकाई है। दूसरी बाइट को जोड़ने की लागत एक इकाई नहीं है, बल्कि 2 इकाइयाँ हैं (पहली बाइट की नकल एक नई स्ट्रिंग ऑब्जेक्ट के साथ-साथ दूसरी बाइट को जोड़ने के लिए)। तीसरे बाइट के लिए 3 यूनिट आदि की लागत की आवश्यकता होती है।
C(N) = 1 + 2 + 3 + ... + N = N(N+1)/2 = O(N^2)
। प्रतीक O(N^2)
को एन स्क्वेर्ड का बिग ओ कहा जाता है, और इसका मतलब है कि लंबे समय में कम्प्यूटेशनल लागत स्ट्रिंग लंबाई के वर्ग के लिए आनुपातिक है। १०० अक्षर बनाने के लिए १०,००० यूनिट काम लेता है, और २०० कैरेक्टर बनाने में ४०,००० यूनिट काम लगता है।
यही कारण है कि 100 वर्णों से 200 वर्ण बनाने में दो बार से अधिक समय लगा। वास्तव में, इसे लंबे समय तक चार बार लेना चाहिए था। हमारा प्रोग्रामिंग अनुभव सही था कि काम को लंबे समय तक तार के लिए थोड़ा और अधिक कुशलता से किया जा रहा है, और इसलिए इसे केवल तीन बार लंबे समय तक लिया गया। एक बार जब फंक्शन कॉल का ओवरहेड नगण्य हो जाता है, तो हम कितनी देर तक एक स्ट्रिंग बना रहे हैं, यह वास्तव में एक स्ट्रिंग को दो बार लंबे समय तक बनाने में चार गुना अधिक समय लेगा।
(ऐतिहासिक नोट: यह विश्लेषण आवश्यक रूप से स्रोत कोड में स्ट्रिंग पर लागू नहीं होता है, जैसे कि html = 'abcd\n' + 'efgh\n' + ... + 'xyz.\n'
, जावास्क्रिप्ट स्रोत कोड कंपाइलर स्ट्रिंग स्ट्रिंग ऑब्जेक्ट में बनाने से पहले एक साथ जुड़ सकते हैं। कुछ साल पहले, KJS कार्यान्वयन। स्रोत कोड के लंबे तारों को लोड करने पर प्लस संकेतों द्वारा जावास्क्रिप्ट को फ्रीज या क्रैश किया जाएगा। चूंकि कम्प्यूटेशनल समय था, O(N^2)
इसलिए वेब पेज बनाना मुश्किल नहीं था, जो कोनकेर वेब ब्राउज़र या सफारी को अधिभारित करता था, जिसमें केजेएस जावास्क्रिप्ट इंजन कोर का उपयोग किया जाता था। जब मैं एक मार्कअप भाषा और जावास्क्रिप्ट मार्कअप भाषा पार्सर विकसित कर रहा था तब इस मुद्दे पर आया था, और तब मुझे पता चला कि जब मैंने जावास्क्रिप्ट के लिए अपनी स्क्रिप्ट लिखी थी तो क्या समस्या थी।)
स्पष्ट रूप से प्रदर्शन का यह तीव्र क्षरण एक बहुत बड़ी समस्या है। हम इसे कैसे निपटा सकते हैं, यह देखते हुए कि हम जावास्क्रिप्ट को अपरिवर्तनीय वस्तुओं के रूप में संभालने के तरीके को नहीं बदल सकते हैं? समाधान एक एल्गोरिथ्म का उपयोग करना है जो स्ट्रिंग को जितनी बार संभव हो उतना फिर से बनाता है।
स्पष्ट करने के लिए, हमारा लक्ष्य छोटे तारों को लंबे तारों से जोड़ने से बचना है, क्योंकि छोटी स्ट्रिंग को जोड़ने के लिए, पूरे लंबे स्ट्रिंग को भी डुप्लिकेट करना होगा।
एल्गोरिथ्म लंबे स्ट्रिंग्स में छोटे तारों को जोड़ने से बचने के लिए कैसे काम करता है
यहां नई स्ट्रिंग ऑब्जेक्ट्स की संख्या को कम करने का एक अच्छा तरीका है। लंबे समय तक एक साथ स्ट्रिंग की लंबाई को मिलाएं ताकि एक बार में एक से अधिक बाइट आउटपुट में जुड़ जाए।
उदाहरण के लिए, लंबाई की एक स्ट्रिंग बनाने के लिए N = 9
:
x = 'x';
s = '';
s += x; /* Now s = 'x' */
x += x; /* Now x = 'xx' */
x += x; /* Now x = 'xxxx' */
x += x; /* Now x = 'xxxxxxxx' */
s += x; /* Now s = 'xxxxxxxxx' as desired */
ऐसा करने के लिए आवश्यक है कि लंबाई 1 की एक स्ट्रिंग बनाई जाए, लंबाई 2 की स्ट्रिंग बनाई जाए, लंबाई 4 की स्ट्रिंग बनाई जाए, लंबाई 8 की स्ट्रिंग बनाई जाए और आखिरकार, लंबाई की स्ट्रिंग बनाते हुए 9. हमने कितनी लागत बचाई है?
पुरानी लागत C(9) = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 9 = 45
।
नई लागत C(9) = 1 + 2 + 4 + 8 + 9 = 24
।
ध्यान दें कि हमें लंबाई 1 की एक स्ट्रिंग को 0 की एक स्ट्रिंग से जोड़ना था, फिर लंबाई 1 की एक स्ट्रिंग की लंबाई 1 तक, फिर लंबाई की एक स्ट्रिंग की लंबाई 2 की एक स्ट्रिंग, फिर लंबाई की एक स्ट्रिंग 4 लंबाई 4 की एक स्ट्रिंग, फिर लंबाई 1 की लंबाई प्राप्त करने के लिए लंबाई 1 से स्ट्रिंग 1 की लंबाई 8। हम जो कर रहे हैं, उसे संक्षेप में लंबी स्ट्रिंग या छोटे तार जोड़ने से बचा जा सकता है। शब्द, समान या लगभग समान लंबाई वाले तार को एक साथ जोड़ने की कोशिश करना।
पुरानी कम्प्यूटेशनल लागत के लिए हमें एक सूत्र मिला N(N+1)/2
। क्या नई लागत के लिए कोई फॉर्मूला है? हां, लेकिन यह जटिल है। महत्वपूर्ण बात यह है कि यह है O(N)
, और इसलिए स्ट्रिंग की लंबाई को दोगुना करना लगभग चौगुना करने के बजाय काम की मात्रा को दोगुना कर देगा।
इस नए विचार को लागू करने वाला कोड कम्प्यूटेशनल लागत के सूत्र के रूप में लगभग जटिल है। जब आप इसे पढ़ते हैं, तो याद रखें कि >>= 1
1 बाइट द्वारा दाईं ओर शिफ्ट करने का मतलब है। तो अगर n = 10011
एक बाइनरी नंबर है, तो n >>= 1
मूल्य में परिणाम होता है n = 1001
।
कोड का दूसरा हिस्सा जिसे आप नहीं पहचान सकते हैं, वह बिटवाइज़ और ऑपरेटर है, जिसे लिखा गया है &
। n & 1
यदि अंतिम बाइनरी अंक n
1 है, और यदि n
0 का अंतिम बाइनरी अंक है, तो यह अभिव्यक्ति सही है।
नया अत्यधिक कुशल stringFill3()
कार्य
function stringFill3(x, n) {
var s = '';
for (;;) {
if (n & 1) s += x;
n >>= 1;
if (n) x += x;
else break;
}
return s;
}
यह अप्रशिक्षित आंख को बदसूरत दिखता है, लेकिन यह प्रदर्शन प्यारा से कम नहीं है।
चलो देखते हैं कि यह फ़ंक्शन कितना अच्छा प्रदर्शन करता है। परिणाम देखने के बाद, यह संभावना है कि आप एक O(N^2)
एल्गोरिथ्म और एल्गोरिथ्म के बीच के अंतर को कभी नहीं O(N)
भूलेंगे।
stringFill1()
200-बाइट स्ट्रिंग बनाने के लिए 88.7 माइक्रोसेकंड (सेकंड का मिलियन) stringFill2()
लेता है, 62.54 लेता है, और stringFill3()
केवल 4.608 लेता है। क्या इस एल्गोरिथ्म इतना बेहतर बना दिया? सभी फ़ंक्शन ने स्थानीय फ़ंक्शन चर का उपयोग करने का लाभ उठाया, लेकिन दूसरे और तीसरे अनुकूलन तकनीकों का लाभ उठाते हुए प्रदर्शन में बीस गुना सुधार किया stringFill3()
।
गहरा विश्लेषण
क्या यह विशेष कार्य पानी से प्रतियोगिता को उड़ा देता है?
जैसा कि मैंने उल्लेख किया है, कारण है कि इन दोनों कार्यों, stringFill1()
औरstringFill2()
, इतनी धीमी गति से चलाना कि जावास्क्रिप्ट तार अपरिवर्तनीय हैं। जावास्क्रिप्ट द्वारा संचित स्ट्रिंग डेटा में एक बार में एक और बाइट की अनुमति देने के लिए मेमोरी को फिर से विभाजित नहीं किया जा सकता है। हर बार एक और बाइट को स्ट्रिंग के अंत में जोड़ा जाता है, पूरे स्ट्रिंग को शुरुआत से अंत तक पुनर्जीवित किया जाता है।
इस प्रकार, स्क्रिप्ट के प्रदर्शन को बेहतर बनाने के लिए, किसी को समय से पहले एक साथ दो तारों को समतल करके लंबे समय तक तार को लंबा करना चाहिए, और फिर पुन: वांछित स्ट्रिंग लंबाई का निर्माण करना चाहिए।
उदाहरण के लिए, 16-अक्षर बाइट स्ट्रिंग बनाने के लिए, पहले एक दो बाइट स्ट्रिंग को प्री-कॉम्पट्यूट किया जाएगा। फिर दो बाइट स्ट्रिंग को एक चार-बाइट स्ट्रिंग को पूर्ववर्ती करने के लिए पुन: उपयोग किया जाएगा। फिर चार-बाइट स्ट्रिंग को आठ बाइट स्ट्रिंग को पूर्ववर्ती करने के लिए पुन: उपयोग किया जाएगा। अंत में, 16 बाइट्स की वांछित नई स्ट्रिंग बनाने के लिए दो आठ-बाइट स्ट्रिंग्स का पुन: उपयोग किया जाएगा। कुल मिलाकर चार नए तार बनाए जाने थे, लंबाई 2 में से एक, लंबाई 4 में से एक, लंबाई 8 में से एक और लंबाई 16 में से एक। कुल लागत 2 + 4 + 8 + 16 = 30 है।
लंबे समय में इस दक्षता की गणना रिवर्स ऑर्डर में जोड़कर की जा सकती है और पहले टर्म a1 = N से शुरू होने वाली और ज्यामिति श्रृंखला का उपयोग करके r = 1/2 का सामान्य अनुपात होता है। एक ज्यामितीय श्रृंखला का योग द्वारा दिया जाता है a_1 / (1-r) = 2N
।
यह लंबाई 2 की एक नई स्ट्रिंग बनाने के लिए एक चरित्र को जोड़ने से अधिक कुशल है, लंबाई 3, 4, 5 की एक नई स्ट्रिंग का निर्माण, और इसी तरह 16 तक। पिछले एल्गोरिथ्म ने एक बार में एक ही बाइट जोड़ने की उस प्रक्रिया का उपयोग किया। , और इसकी कुल लागत होगी n (n + 1) / 2 = 16 (17) / 2 = 8 (17) = 136
।
जाहिर है, 136 30 की तुलना में बहुत अधिक संख्या है, और इसलिए पिछले एल्गोरिथ्म में एक स्ट्रिंग बनाने के लिए बहुत अधिक समय लगता है।
दो तरीकों की तुलना करने के लिए आप देख सकते हैं कि कितनी तेजी से पुनरावर्ती एल्गोरिथ्म (जिसे "विभाजित और जीतना" भी कहा जाता है) लंबाई 123,457 की एक स्ट्रिंग पर है। मेरे FreeBSD कंप्यूटर पर, stringFill3()
फ़ंक्शन में कार्यान्वित यह एल्गोरिथ्म 0.001058 सेकंड में स्ट्रिंग बनाता है, जबकि मूल stringFill1()
फ़ंक्शन 0.0808 सेकंड में स्ट्रिंग बनाता है। नया फंक्शन 76 गुना तेज है।
प्रदर्शन में अंतर बढ़ता है क्योंकि स्ट्रिंग की लंबाई बड़ी हो जाती है। सीमा में जैसा कि बड़ा और बड़ा स्ट्रिंग्स बनाया जाता है, मूल फ़ंक्शन लगभग C1
(निरंतर) बार की तरह व्यवहार करता है N^2
, और नया फ़ंक्शन C2
बार (निरंतर) बार की तरह व्यवहार करता है N
।
हमारे प्रयोग से हम C1
होने C1 = 0.0808 / (123457)2 = .00000000000530126997
का मूल्य, और होने का मूल्य निर्धारित कर सकते C2
हैं C2 = 0.001058 / 123457 = .00000000856978543136
। 10 सेकंड में, नया फ़ंक्शन 1,166,890,359 वर्णों वाला एक स्ट्रिंग बना सकता है। इसी तार को बनाने के लिए, पुराने फ़ंक्शन को 7,218,384 सेकंड की आवश्यकता होगी।
यह दस सेकंड की तुलना में लगभग तीन महीने है!
मैं केवल उत्तर दे रहा हूं (कई साल देर से) क्योंकि इस समस्या का मेरा मूल समाधान 10 से अधिक वर्षों से इंटरनेट के आसपास तैर रहा है, और जाहिर तौर पर अभी भी कुछ लोगों द्वारा इसे याद नहीं किया जाता है। मैंने सोचा था कि इसके बारे में एक लेख लिखकर मैं यहाँ मदद करूँगा:
उच्च गति जावास्क्रिप्ट / पृष्ठ 3 के लिए प्रदर्शन अनुकूलन
दुर्भाग्य से, यहां प्रस्तुत किए गए कुछ अन्य समाधान अभी भी उनमें से कुछ हैं जो आउटपुट की समान मात्रा का उत्पादन करने में तीन महीने लगते हैं जो एक उचित समाधान 10 सेकंड में बनाता है।
मैं स्टैक ओवरफ्लो पर एक कैनोनिकल जवाब के रूप में लेख के हिस्से को पुन: पेश करने के लिए समय लेना चाहता हूं।
ध्यान दें कि यहां सबसे अच्छा प्रदर्शन करने वाला एल्गोरिथ्म स्पष्ट रूप से मेरे एल्गोरिथ्म पर आधारित है और संभवतः किसी और की तीसरी या चौथी पीढ़ी के अनुकूलन से विरासत में मिला था। दुर्भाग्य से, संशोधनों के परिणामस्वरूप इसके प्रदर्शन को कम किया गया। यहाँ प्रस्तुत मेरे समाधान की भिन्नता ने शायद मेरी भ्रमित for (;;)
अभिव्यक्ति को नहीं समझा , जो सी में लिखे सर्वर के मुख्य अनंत लूप की तरह दिखता है, और जिसे केवल लूप नियंत्रण के लिए सावधानी से तैनात ब्रेक स्टेटमेंट की अनुमति देने के लिए डिज़ाइन किया गया था, सबसे कॉम्पैक्ट तरीका तेजी से एक अतिरिक्त अनावश्यक समय की नकल करने से बचें।