এই সমস্যাটি জাভাস্ক্রিপ্টের জন্য একটি সুপরিচিত / "ক্লাসিক" অপ্টিমাইজেশন ইস্যু, যা জাভাস্ক্রিপ্টের স্ট্রিংগুলি "অপরিবর্তনীয়" এবং একটি স্ট্রিংয়ের জন্য একটি একক অক্ষরকেও যুক্ত করে তৈরি করার প্রয়োজন রয়েছে, মেমরির জন্য বরাদ্দ এবং অনুলিপি সহ , একটি সম্পূর্ণ নতুন স্ট্রিং।
দুর্ভাগ্যক্রমে, এই পৃষ্ঠায় স্বীকৃত উত্তরটি ভুল, যেখানে "ভুল" এর অর্থ সহজ এক-চরিত্রের স্ট্রিংয়ের জন্য 3x এর পারফরম্যান্স ফ্যাক্টর এবং ছোট্ট স্ট্রিংগুলির জন্য 8x-97x দ্বারা আরও বারবার পুনরাবৃত্তি করা, বাক্য পুনরাবৃত্তি করার জন্য 300x, এবং অসীম ভুল যখন অ্যালগরিদমের জটিলতার অনুপাতের n
সীমাটিকে অনন্তের দিকে নিয়ে যাওয়া। এছাড়াও, এই পৃষ্ঠায় আরও একটি উত্তর রয়েছে যা প্রায় সঠিক (গত 13 বছরে ইন্টারনেটে প্রচলিত সঠিক সমাধানের বহু প্রজন্মের একটির এবং তারতম্যের ভিত্তিতে)। যাইহোক, এই "প্রায় সঠিক" সমাধানটি 50% কার্যক্ষমতা হ্রাসের কারণ হিসাবে সঠিক অ্যালগরিদমের মূল পয়েন্টটি মিস করে।
গৃহীত উত্তরের জন্য জেএস পারফরম্যান্স ফলাফল, শীর্ষস্থানীয় অন্য উত্তর (এই উত্তরের মূল অ্যালগরিদমের একটি অবনমিত সংস্করণের উপর ভিত্তি করে), এবং এই উত্তরটি 13 বছর আগে তৈরি আমার অ্যালগরিদম ব্যবহার করে
~ অক্টোবর 2000 আমি এই সঠিক সমস্যার জন্য একটি অ্যালগরিদম প্রকাশ করি যা ব্যাপকভাবে অভিযোজিত, সংশোধিত, পরে অবশেষে খারাপভাবে বোঝা যায় এবং ভুলে যায়। এই সমস্যার প্রতিকারের জন্য, ২০০৮ সালের আগস্টে আমি একটি নিবন্ধ প্রকাশ করেছি http://www.webreferences.com/programming/javascript/jkm3/3.html অ্যালগোরিদম ব্যাখ্যা করে এবং সাধারণ-উদ্দেশ্যমূলক জাভাস্ক্রিপ্ট অপ্টিমাইজেশনের সাধারণ উদাহরণ হিসাবে এটি ব্যবহার করে। এতক্ষণে , ওয়েব তথ্যসূত্রটি আমার যোগাযোগের তথ্য এবং এই নিবন্ধ থেকে আমার নামও স্ক্র্যাব করেছে। এবং আবারও, অ্যালগরিদমটি ব্যাপকভাবে অভিযোজিত, সংশোধিত, তারপরে দুর্বলভাবে বোঝা যায় এবং মূলত ভুলে যায়।
আসল স্ট্রিং পুনরাবৃত্তি / জোসেফ মায়ারস দ্বারা জাভাস্ক্রিপ্ট অ্যালগরিদম, পাঠ্য .js এর মধ্যে পাঠ্য গুণিত ফাংশন হিসাবে Y2K; ওয়েব রেফারেন্স দ্বারা এই ফর্মটিতে আগস্ট, ২০০ published প্রকাশিত:
http://www.webreferences.com/programming/javascript/jkm3/3.html (নিবন্ধটি জাভাস্ক্রিপ্ট অপ্টিমাইজেশনের উদাহরণ হিসাবে ফাংশনটি ব্যবহার করেছে, যা কেবলমাত্র অদ্ভুতের জন্য নাম "স্ট্রিংফিল 3।")
/*
* 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()
একক অক্ষরের পুনরাবৃত্তি ব্যতীত ফাংশনটির প্রয়োগকে সীমাবদ্ধ করে তোলে। এমনকি একক অক্ষর ব্যবহার করা যায় না যদি সেগুলিতে এইচটিএমএল সত্তার মতো একাধিক বাইট থাকে
।
কোনও অবজেক্ট প্রোপার্টিটির এই অপ্রয়োজনীয় ব্যবহারের ফলে সবচেয়ে খারাপ সমস্যাটি হ'ল খালি ইনপুট স্ট্রিংয়ে পরীক্ষা করা হলে ফাংশনটি একটি অসীম লুপ তৈরি করে 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()
আমাদের প্রথম দুটি অপটিমাইজেশন ব্যবহারের কারণে উন্নত ফাংশনটি আরও দ্রুত। আপনি কি বিশ্বাস করবেন যদি আমি আপনাকে বলি যে এটি এখনকার তুলনায় অনেক গুণ দ্রুত উন্নতি করা যায়?
হ্যাঁ, আমরা সেই লক্ষ্যটি অর্জন করতে পারি। এখনই আমাদের কীভাবে সংক্ষিপ্ত স্ট্রিংগুলিকে দীর্ঘ স্ট্রিংগুলিতে সংযোজন করা উচিত তা বোঝানোর দরকার explain
স্বল্প-মেয়াদী আচরণটি আমাদের আসল কার্যের তুলনায় বেশ ভাল বলে মনে হচ্ছে। কম্পিউটার বিজ্ঞানীরা কোনও ফাংশন বা কম্পিউটার প্রোগ্রাম অ্যালগরিদমের "অ্যাসিপটোটিক আচরণ" বিশ্লেষণ করতে পছন্দ করেন, যার অর্থ বৃহত ইনপুটগুলির সাথে এটি পরীক্ষা করে দীর্ঘমেয়াদী আচরণটি অধ্যয়ন করা। কখনও কখনও আরও পরীক্ষা না করে, কেউ কখনই কম্পিউটার প্রোগ্রামের উন্নতি করতে পারে সে সম্পর্কে সচেতন হয় না। কী হবে তা দেখতে, আমরা একটি 200-বাইট স্ট্রিং তৈরি করতে যাচ্ছি।
যে সমস্যাটি দেখা যাচ্ছে stringFill2()
আমাদের টাইমিং ফাংশনটি ব্যবহার করে আমরা দেখতে পেলাম যে 200 বাইট স্ট্রিংয়ের জন্য সময়টি 100 বাইট স্ট্রিংয়ের 27.68 এর তুলনায়, 62.54 মাইক্রোসেকেন্ডে বেড়ে যায়। মনে হচ্ছে দ্বিগুণ কাজ করার জন্য সময়টি দ্বিগুণ করা উচিত, তবে পরিবর্তে এটি তিনগুণ বা চারগুণ হয়েছে। প্রোগ্রামিংয়ের অভিজ্ঞতা থেকে, এই ফলাফলটি অদ্ভুত বলে মনে হচ্ছে, কারণ যদি কিছু হয় তবে কাজটি আরও দক্ষতার সাথে করা হওয়ায় ফাংশনটি কিছুটা দ্রুত হওয়া উচিত (ফাংশন কলের জন্য 200 বাইট প্রতি ফাংশন কলের পরিবর্তে 100 বাইটের চেয়ে 100 জন) call এই ইস্যুটির জাভাস্ক্রিপ্ট স্ট্রিংগুলির একটি কুখ্যাত সম্পত্তি রয়েছে: জাভাস্ক্রিপ্ট স্ট্রিংগুলি "অপরিবর্তনীয়"।
অপরিহার্য অর্থ হ'ল একবার স্ট্রিং তৈরি হয়ে গেলে আপনি পরিবর্তন করতে পারবেন না। একবারে একটি বাইট যুক্ত করে, আমরা আরও একটি প্রচেষ্টা বাইট ব্যবহার করছি না। আমরা আসলে পুরো স্ট্রিং প্লাস আরও একটি বাইট পুনরুদ্ধার করছি।
কার্যত, 100 বাইট স্ট্রিংয়ে আরও একটি বাইট যুক্ত করতে, এটি 101 বাইটের কাজের মূল্য নেয়। আসুন সংক্ষেপে N
বাইটগুলির একটি স্ট্রিং তৈরি করার জন্য গণনা ব্যয় বিশ্লেষণ করা যাক । প্রথম বাইট যুক্ত করার ব্যয় গণ্য প্রচেষ্টার 1 ইউনিট। দ্বিতীয় বাইট যুক্ত করার ব্যয় এক ইউনিট নয় 2 ইউনিট (প্রথম বাইটটি একটি নতুন স্ট্রিং অবজেক্টে অনুলিপি করার পাশাপাশি দ্বিতীয় বাইট যুক্ত করার)। তৃতীয় বাইটের জন্য 3 ইউনিট ইত্যাদির জন্য প্রয়োজন cost
C(N) = 1 + 2 + 3 + ... + N = N(N+1)/2 = O(N^2)
। প্রতীকটি O(N^2)
এন স্কোয়ারের বিগ হে হিসাবে উচ্চারণ করা হয় এবং এর অর্থ দীর্ঘমেয়াদে গণনা ব্যয় স্ট্রিং দৈর্ঘ্যের বর্গক্ষেত্রের সমানুপাতিক। 100 টি অক্ষর তৈরি করতে 10,000 ইউনিট কাজ লাগে এবং 200 টি অক্ষর তৈরি করতে 40,000 ইউনিট কাজ লাগে।
এজন্য 100 টি চরিত্রের চেয়ে 200 টি অক্ষর তৈরি করতে দ্বিগুণের বেশি সময় লেগেছিল। আসলে, এটি দীর্ঘ হিসাবে চার বার নেওয়া উচিত ছিল। আমাদের প্রোগ্রামিংয়ের অভিজ্ঞতাটি সঠিক ছিল যে দীর্ঘতর স্ট্রিংয়ের জন্য কাজটি কিছুটা দক্ষতার সাথে করা হচ্ছে, এবং তাই এটি প্রায় তিনবার বেশি সময় নিয়েছে। ফাংশন কলের ওভারহেড একবার স্ট্রিংয়ের কতক্ষণ তৈরি করা যায় তা তুচ্ছ হয়ে উঠলে, তার চেয়ে দ্বিগুণ দীর্ঘ স্ট্রিং তৈরি করতে চার গুণ বেশি সময় লাগবে।
( html = 'abcd\n' + 'efgh\n' + ... + 'xyz.\n'
Noteতিহাসিক দ্রষ্টব্য: এই বিশ্লেষণটি উত্স কোডের স্ট্রিংগুলিতে অগত্যা প্রযোজ্য না, যেমন জাভাস্ক্রিপ্ট উত্স কোড সংকলক জাভাস্ক্রিপ্ট স্ট্রিং অবজেক্টে পরিণত করার আগে স্ট্রিংগুলিতে যোগ দিতে পারে। মাত্র কয়েক বছর আগে, কেজেএস বাস্তবায়ন জাভাস্ক্রিপ্ট হ'ল বা ক্রস হয়ে যাবে যখন উত্স কোডের দীর্ঘ স্ট্রিংগুলি প্লাস চিহ্নগুলির সাথে যুক্ত হয়েছিল। গণনার সময় যেহেতু O(N^2)
ওয়েব পৃষ্ঠাগুলি তৈরি করা কঠিন ছিল না যা কনকরার ওয়েব ব্রাউজার বা সাফারি, যা কেজেএস জাভাস্ক্রিপ্ট ইঞ্জিন কোর ব্যবহার করেছিল over আমি যখন একটি মার্কআপ ভাষা এবং জাভাস্ক্রিপ্ট মার্কআপ ল্যাঙ্গুয়েজ পার্সার বিকাশ করছিলাম তখন এই সমস্যাটি জুড়ে এসেছিল এবং তারপরে আমি যখন জাভাস্ক্রিপ্ট অন্তর্ভুক্তের জন্য আমার স্ক্রিপ্ট লিখেছিলাম তখন সমস্যাটি কি ঘটছে তা আবিষ্কার করেছি))
স্পষ্টত পারফরম্যান্সের এই দ্রুত অবক্ষয় একটি বিশাল সমস্যা। আমরা জাভাস্ক্রিপ্টের অপরিবর্তনীয় বস্তু হিসাবে স্ট্রিংগুলি পরিচালনা করার পদ্ধতি পরিবর্তন করতে পারি না তা দিয়ে এটি কীভাবে মোকাবেলা করতে পারি? সমাধানটি হল একটি অ্যালগরিদম ব্যবহার করা যা স্ট্রিংটিকে যতবার সম্ভব পুনরায় তৈরি করে।
স্পষ্ট করার জন্য, আমাদের লক্ষ্যটি দীর্ঘ স্ট্রিংগুলিতে সংক্ষিপ্ত স্ট্রিং যুক্ত করা এড়ানো, যেহেতু সংক্ষিপ্ত স্ট্রিং যুক্ত করতে, পুরো দীর্ঘ স্ট্রিংটিকেও নকল করতে হবে।
দীর্ঘ স্ট্রিংগুলিতে সংক্ষিপ্ত স্ট্রিংগুলি এড়ানোর জন্য কীভাবে অ্যালগরিদম কাজ করে
নতুন স্ট্রিং অবজেক্টগুলি তৈরি হওয়ার সংখ্যা হ্রাস করার জন্য এখানে একটি ভাল উপায়। দীর্ঘতর দৈর্ঘ্যের স্ট্রিং একসাথে সংঘবদ্ধ করুন যাতে একবারে একাধিক বাইট আউটপুটে যুক্ত হয়।
উদাহরণস্বরূপ, দৈর্ঘ্যের একটি স্ট্রিং তৈরি করতে 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
।
নোট করুন যে আমাদের দৈর্ঘ্যের 0 টি দৈর্ঘ্যের একটি স্ট্রিংয়ের সাথে 1 দৈর্ঘ্যের একটি স্ট্রিং যোগ করতে হবে, তারপরে 1 দৈর্ঘ্যের একটি স্ট্রিং 1 দৈর্ঘ্যের একটি স্ট্রিং, তারপরে দৈর্ঘ্যের একটি স্ট্রিং 2 দৈর্ঘ্যের একটি স্ট্রিং, তারপরে 4 দৈর্ঘ্যের একটি স্ট্রিং যোগ করতে হয়েছিল দৈর্ঘ্যের একটি স্ট্রিং, তারপরে দৈর্ঘ্যের একটি স্ট্রিং দৈর্ঘ্যের একটি স্ট্রিং প্রাপ্ত করার জন্য 9 দৈর্ঘ্যের একটি স্ট্রিং 9 দৈর্ঘ্যের একটি স্ট্রিং প্রাপ্ত করার জন্য সংক্ষিপ্ত আকারে বলা যেতে পারে যে আমরা দীর্ঘ স্ট্রিংগুলিতে সংক্ষিপ্ত স্ট্রিং যুক্ত করা এড়ানো বা অন্যটিতে শব্দগুলি, সমান বা প্রায় সমান দৈর্ঘ্যের স্ট্রিংগুলি একসাথে আঁকতে চেষ্টা করে।
পুরানো গণনার জন্য আমরা একটি সূত্র পেয়েছি N(N+1)/2
। নতুন ব্যয়ের জন্য কি কোনও সূত্র আছে? হ্যাঁ, তবে এটি জটিল। গুরুত্বপূর্ণ বিষয়টি এটি হ'ল O(N)
, এবং স্ট্রিংয়ের দৈর্ঘ্য দ্বিগুণ করা প্রায় চারগুণ না হয়ে কাজের পরিমাণ দ্বিগুণ করবে।
এই নতুন ধারণাটি কার্যকর করে এমন কোডটি গণনা ব্যয়ের সূত্রের মতো প্রায় জটিল। আপনি যখন এটি পড়েন, মনে রাখবেন যে >>= 1
1 বাইট দ্বারা ডান স্থানান্তরিত করার অর্থ। সুতরাং যদি n = 10011
একটি বাইনারি সংখ্যা হয়, তবে n >>= 1
মানটি ফলাফল করে n = 1001
।
আপনি যে কোডটি চিনতে পারবেন না তার অন্য অংশটি হ'ল বিটওয়াইজ এবং অপারেটর, লিখিত &
। n & 1
সর্বশেষ বাইনারি অঙ্ক 1 হলে এক্সপ্রেশনটি সত্য n
, এবং শেষ বাইনারি 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 বাইটের পছন্দসই নতুন স্ট্রিং তৈরি করতে। সামগ্রিকভাবে চারটি নতুন স্ট্রিং তৈরি করতে হয়েছিল, দৈর্ঘ্যের একটি, দৈর্ঘ্যের একটি, 4 দৈর্ঘ্যের একটি, 8 দৈর্ঘ্যের একটি এবং দৈর্ঘ্যের একটি 16. মোট ব্যয় 2 + 4 + 8 + 16 = 30।
দীর্ঘ মেয়াদে এই দক্ষতাটি বিপরীত ক্রমে যুক্ত করে এবং প্রথম শর্ত a1 = N দিয়ে শুরু করে জ্যামিতিক সিরিজ ব্যবহার করে এবং আর = 1/2 এর একটি সাধারণ অনুপাত রেখে গণনা করা যেতে পারে। একটি জ্যামিতিক সিরিজের যোগফল দেওয়া হয়েছে a_1 / (1-r) = 2N
।
এটি দৈর্ঘ্যের একটি নতুন স্ট্রিং তৈরি করতে একটি অক্ষর যুক্ত করার চেয়ে আরও দক্ষ, 3, 4, 5 এবং আরও দীর্ঘ দৈর্ঘ্যের একটি নতুন স্ট্রিং তৈরি করে 16 অবধি The , এবং এটির মোট ব্যয় হবে n (n + 1) / 2 = 16 (17) / 2 = 8 (17) = 136
।
স্পষ্টতই, 136 30 এর চেয়ে অনেক বেশি সংখ্যক সংখ্যা এবং তাই পূর্ববর্তী অ্যালগরিদম স্ট্রিং তৈরি করতে অনেক বেশি সময় নেয়।
দুটি পদ্ধতির তুলনা করতে আপনি 123,457 দৈর্ঘ্যের একটি স্ট্রিংতে পুনরাবৃত্ত অ্যালগরিদমকে ("বিভাজন এবং বিজয়" নামেও পরিচিত) কত দ্রুত তা দেখতে পারেন। আমার ফ্রিবিএসডি কম্পিউটারে 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 সেকেন্ডে তৈরি করে এমন পরিমাণ আউটপুট তৈরি করতে তিন মাস সময় লাগে would
আমি স্ট্যাক ওভারফ্লো সম্পর্কে একটি ক্যানোনিকাল উত্তর হিসাবে নিবন্ধটির অংশটি পুনরুত্পাদন করতে সময় নিতে চাই।
মনে রাখবেন যে এখানে সেরা-সম্পাদনকারী অ্যালগরিদম স্পষ্টভাবে আমার অ্যালগরিদমের উপর ভিত্তি করে এবং সম্ভবত অন্য কারও তৃতীয় বা চতুর্থ প্রজন্মের অভিযোজন থেকে উত্তরাধিকার সূত্রে প্রাপ্ত হয়েছিল। দুর্ভাগ্যক্রমে, পরিবর্তনগুলির ফলে এর কার্যকারিতা হ্রাস পেয়েছে। এখানে উপস্থাপিত আমার সমাধানটির বৈকল্পিকতা সম্ভবত আমার বিভ্রান্তিকর ভাবটি বুঝতে পারে নি for (;;)
যা দেখতে সিটিতে লিখিত কোনও সার্ভারের মূল অসীম লুপের মতো, এবং যা কেবল লুপ নিয়ন্ত্রণের জন্য সাবধানতার সাথে অবস্থানযুক্ত ব্রেক স্টেটমেন্টকে অনুমতি দেওয়ার জন্য ডিজাইন করা হয়েছিল, এটির সবচেয়ে সংক্ষিপ্ত উপায় to স্ট্রিংটির এক অতিরিক্ত অপ্রয়োজনীয় সময়টিকে দ্রুতভাবে এড়িয়ে চলুন।