গিট ব্রাঞ্চিং বাংলায় বোঝা | Git Branching Tutorial
গিট ব্রাঞ্চিং বাংলায় বোঝা
গিট ব্রাঞ্চিং সম্পর্কে সহজে বোঝার জন্য বাংলায় বিস্তারিত গাইড।
গিট ব্রাঞ্চ কী?
ধরুন, আপনার একটা গাছ আছে। গাছের মূল ডালটা হলো main ব্রাঞ্চ। এখন আপনি যদি গাছের একটা নতুন ডাল বাড়াতে চান, যেখানে আপনি নতুন কিছু চেষ্টা করবেন, সেটাই হলো একটা নতুন "ব্রাঞ্চ"। এই নতুন ব্রাঞ্চে আপনি যা খুশি পরিবর্তন করতে পারেন, কিন্তু মূল ডাল অপরিবর্তিত থাকবে।
কেন ব্রাঞ্চ ব্যবহার করবেন?
নতুন ফিচার যোগ করা: একটা নতুন ফিচার যোগ করতে চাইলে আলাদা ব্রাঞ্চে কাজ করুন।
বাগ ফিক্স করা: সমস্যা ঠিক করতে আলাদা ব্রাঞ্চ ব্যবহার করুন।
টিমে একসঙ্গে কাজ: অনেকে একসঙ্গে কাজ করলে প্রত্যেকে নিজের ব্রাঞ্চে কাজ করতে পারে।
কীভাবে ব্রাঞ্চ তৈরি করবেন?
১. বর্তমান ব্রাঞ্চ চেক করা
git branch
এটা দেখাবে আপনি কোন ব্রাঞ্চে আছেন।
২. নতুন ব্রাঞ্চ তৈরি করা
git branch feature-1
৩. নতুন ব্রাঞ্চে যাওয়া
git checkout feature-1
অথবা এক লাইনে:
git checkout -b feature-1
৪. কাজ করা এবং কমিট করা
git add .
git commit -m "নতুন ফিচার যোগ করা হয়েছে"
৫. মূল ব্রাঞ্চে মার্জ করা
git checkout main
git merge feature-1
৬. ব্রাঞ্চ ডিলিট করা
git branch -d feature-1
উদাহরণ
ধরুন, আপনার একটা ওয়েবসাইটে "লগইন" ফিচার যোগ করতে চান:
git checkout -b login-feature দিয়ে ব্রাঞ্চ তৈরি করুন।
কোড লিখুন এবং কমিট করুন।
git checkout main এবং git merge login-feature করুন।
C++ adjacent_find() ফাংশন - বাংলায় ব্যাখ্যা ও উদাহরণ
C++ এর adjacent_find() ফাংশন - বাংলায় ব্যাখ্যা
লেখক: Shakil Babu | তারিখ: ২৯ মার্চ, ২০২৫
adjacent_find() কী?
C++ এর <algorithm> লাইব্রেরিতে adjacent_find() একটি ফাংশন যা কোনো রেঞ্জের (যেমন: ভেক্টর, অ্যারে) মধ্যে দুটি পাশাপাশি উপাদান খুঁজে বের করে, যারা একই মান ধারণ করে বা কোনো নির্দিষ্ট শর্ত পূরণ করে। এটি প্রথম জোড়া পাশাপাশি উপাদানের ইটারেটর রিটার্ন করে। যদি এমন কোনো জোড়া না পাওয়া যায়, তবে রেঞ্জের শেষ ইটারেটর রিটার্ন করে।
সিনট্যাক্স
adjacent_find (ForwardIterator first, ForwardIterator last);
// অথবা
adjacent_find (ForwardIterator first, ForwardIterator last, BinaryPredicate pred);
first: রেঞ্জের শুরুর ইটারেটর।
last: রেঞ্জের শেষের ইটারেটর।
pred: একটি বাইনারি প্রেডিকেট (ঐচ্ছিক), যা দুটি উপাদানের মধ্যে তুলনা করে।
উদাহরণগুলো বাংলায় ব্যাখ্যা
উদাহরণ ১: ডিফল্ট adjacent_find()
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> v = {1, 2, 2, 3, 4, 5};
auto it = adjacent_find(v.begin(), v.end());
if (it != v.end()) {
cout << "পাশাপাশি একই উপাদান পাওয়া গেছে: " << *it << endl;
} else {
cout << "কোনো পাশাপাশি একই উপাদান নেই" << endl;
}
return 0;
}
আউটপুট: পাশাপাশি একই উপাদান পাওয়া গেছে: ২
ব্যাখ্যা: এখানে ভেক্টরে {1, 2, 2, 3, 4, 5} আছে। adjacent_find() দেখেছে যে ২ এবং ২ পাশাপাশি আছে। তাই প্রথম ২-এর ইটারেটর রিটার্ন করেছে।
উদাহরণ ২: কোনো পাশাপাশি একই উপাদান না থাকলে
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> v = {1, 3, 5, 7};
auto it = adjacent_find(v.begin(), v.end());
if (it != v.end()) {
cout << "পাশাপাশি একই উপাদান পাওয়া গেছে: " << *it << endl;
} else {
cout << "কোনো পাশাপাশি একই উপাদান নেই" << endl;
}
return 0;
}
আউটপুট: কোনো পাশাপাশি একই উপাদান নেই
ব্যাখ্যা: এখানে {1, 3, 5, 7}-এ কোনো পাশাপাশি একই সংখ্যা নেই। তাই v.end() রিটার্ন করেছে।
উদাহরণ ৩: কাস্টম প্রেডিকেট ব্যবহার
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool sumEqualToTen(int a, int b) {
return (a + b == 10);
}
int main() {
vector<int> v = {3, 7, 4, 6, 5};
auto it = adjacent_find(v.begin(), v.end(), sumEqualToTen);
if (it != v.end()) {
cout << "পাশাপাশি উপাদান যার সমষ্টি ১০: " << *it << " এবং " << *(it + 1) << endl;
} else {
cout << "এমন কোনো জোড়া নেই" << endl;
}
return 0;
}
আউটপুট: পাশাপাশি উপাদান যার সমষ্টি ১০: ৩ এবং ৭
ব্যাখ্যা: এখানে {3, 7, 4, 6, 5}-এ ৩ এবং ৭-এর সমষ্টি ১০। তাই প্রথম উপাদান ৩-এর ইটারেটর রিটার্ন করেছে।
ফ্রিকোয়েন্সি কাউন্টার অ্যালগরিদম: বাংলায় বিস্তারিত গাইড ও উদাহরণ
ফ্রিকোয়েন্সি কাউন্টার অ্যালগরিদম: বাংলায় বিস্তারিত গাইড
ফ্রিকোয়েন্সি কাউন্টার অ্যালগরিদম কী?
ফ্রিকোয়েন্সি কাউন্টার অ্যালগরিদম হলো একটি কার্যকর পদ্ধতি যা কোনো ডেটা সেটে প্রতিটি উপাদানের উপস্থিতির সংখ্যা গণনা করে। এটি সাধারণত অ্যারে, স্ট্রিং বা অন্যান্য ডেটা স্ট্রাকচারে প্রতিটি আইটেমের ফ্রিকোয়েন্সি নির্ণয়ে ব্যবহৃত হয়। এই অ্যালগরিদমটি দ্রুত এবং দক্ষতার সাথে কাজ করে।
কেন ফ্রিকোয়েন্সি কাউন্টার ব্যবহার করা হয়?
এই অ্যালগরিদমটি সময় জটিলতা কমাতে এবং ডেটা বিশ্লেষণে দ্রুত ফলাফল প্রদানে ব্যবহৃত হয়। উদাহরণস্বরূপ, কোনো তালিকায় কোনো সংখ্যা বা অক্ষর কতবার আছে তা জানতে এটি অত্যন্ত উপযোগী। এটি প্যাটার্ন ম্যাচিং, ডুপ্লিকেট চেকিং এবং ডেটা প্রস্তুতিতে গুরুত্বপূর্ণ ভূমিকা পালন করে।
কখন এটি ব্যবহার করবেন?
যখন আপনার কাছে একটি ডেটা সেট থাকবে এবং আপনি জানতে চাইবেন প্রতিটি উপাদান কতবার পুনরাবৃত্তি হয়েছে, তখন এই অ্যালগরিদমটি ব্যবহার করা হয়। উদাহরণস্বরূপ:
একটি স্ট্রিংয়ে প্রতিটি অক্ষরের সংখ্যা গণনা।
একটি অ্যারেতে ডুপ্লিকেট উপাদান খুঁজে বের করা।
দুটি ডেটা সেটের তুলনা করা।
বিস্তারিত ব্যাখ্যা: শুরু থেকে অ্যাডভান্সড লেভেল
এই অ্যালগরিদমটি ধাপে ধাপে বোঝার জন্য আমরা পাইথনে কোডের মাধ্যমে ৫টি উদাহরণ দেখব।
ধাপ ১: মৌলিক ধারণা
এই অ্যালগরিদমে আমরা একটি ডিকশনারি (হ্যাশ ম্যাপ) ব্যবহার করি। প্রতিটি উপাদানকে কী এবং এর ফ্রিকোয়েন্সিকে মান হিসেবে সংরক্ষণ করা হয়।
উদাহরণ ১: স্ট্রিংয়ে অক্ষর গণনা
def count_frequency(string):
freq = {}
for char in string:
if char in freq:
freq[char] += 1
else:
freq[char] = 1
return freq
text = "বাংলা"
result = count_frequency(text)
print(result)
আউটপুট: {'ব': 1, 'া': 2, 'ং': 1, 'ল': 1}
ব্যাখ্যা: "বাংলা" স্ট্রিংয়ে "া" দুইবার এসেছে, বাকি অক্ষর একবার করে।
উদাহরণ ২: লিস্টে সংখ্যা গণনা
def count_numbers(arr):
freq = {}
for num in arr:
freq[num] = freq.get(num, 0) + 1
return freq
numbers = [1, 2, 3, 2, 1, 4]
result = count_numbers(numbers)
print(result)
আউটপুট: {1: 2, 2: 2, 3: 1, 4: 1}
ব্যাখ্যা: ১ এবং ২ দুইবার করে এসেছে, ৩ এবং ৪ একবার করে।
ফ্রিকোয়েন্সি কাউন্টার অ্যালগরিদম একটি শক্তিশালী এবং দক্ষ টুল যা সহজ থেকে জটিল সমস্যা সমাধানে ব্যবহৃত হয়। এটি O(n) সময় জটিলতায় কাজ করে এবং হ্যাশ ম্যাপের সাহায্যে দ্রুত ফলাফল দেয়। এই গাইড এবং উদাহরণগুলো আপনার কোডিং দক্ষতা বাড়াতে সাহায্য করবে।
Beautiful Matrix সমস্যার সি++ সমাধান - বাংলা ব্যাখ্যা
Beautiful Matrix সমস্যার সি++ সমাধান - বাংলা ব্যাখ্যা
এই পোস্টে আমরা "Beautiful Matrix" নামের একটি প্রোগ্রামিং সমস্যার সমাধান করবো সি++ ভাষায়। সমস্যাটির উদ্দেশ্য হলো একটি ৫×৫ ম্যাট্রিক্সে একটি "১" কে মাঝখানে (৩য় সারি, ৩য় কলাম) নিয়ে আসার জন্য সর্বনিম্ন কতগুলো মুভ লাগবে তা বের করা। প্রতিটি মুভে আমরা পাশাপাশি দুটি সারি বা কলাম অদলবদল করতে পারি। এখানে আমি আমার সমাধানের প্রতিটি লাইন বাংলায় বিস্তারিতভাবে ব্যাখ্যা করবো।
সমস্যার বিবরণ
আমাদের দেওয়া আছে একটি ৫×৫ ম্যাট্রিক্স যেখানে ২৪টি শূন্য (০) এবং একটি এক (১) আছে। আমাদের লক্ষ্য হলো এই "১" কে ম্যাট্রিক্সের মাঝখানে (৩য় সারি, ৩য় কলাম, বা ০-ভিত্তিক ইনডেক্সে ২,২) নিয়ে আসা। আমরা শুধু পাশের সারি বা কলাম অদলবদল করতে পারি।
সি++ কোড
#include <bits/stdc++.h>
using namespace std;
int main() {
const int n = 5;
vector<vector<int>> matrix(n, vector<int>(n));
int row, col;
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
cin >> matrix[i][j];
if(matrix[i][j] == 1) {
row = i;
col = j;
}
}
}
int moves = abs(row - 2) + abs(col - 2);
cout << moves << endl;
return 0;
}
লাইন বাই লাইন বাংলা ব্যাখ্যা
লাইন ১: #include <bits/stdc++.h>
এই লাইনটি সি++ এর স্ট্যান্ডার্ড লাইব্রেরির সব হেডার ফাইল ইনক্লুড করে। এটি আমাদের প্রয়োজনীয় সব ফাংশন যেমন vector, cin, cout, abs ইত্যাদি ব্যবহার করতে দেয়। এটি একটি শর্টকাট যা সাধারণত প্রতিযোগিতামূলক প্রোগ্রামিংয়ে ব্যবহৃত হয়।
লাইন ২: using namespace std;
এটি আমাদের std নেমস্পেসের সবকিছু সরাসরি ব্যবহার করার অনুমতি দেয়। যেমন, আমাদের std::cin এর পরিবর্তে শুধু cin লিখতে হবে।
লাইন ৪: int main() {
এটি প্রোগ্রামের মূল ফাংশন। প্রোগ্রাম শুরু হয় এখান থেকে এবং এটি একটি ইন্টিজার (পূর্ণসংখ্যা) রিটার্ন করে।
লাইন ৫: const int n = 5;
এখানে আমরা একটি ধ্রুবক (constant) ভেরিয়েবল n ডিফাইন করছি যার মান ৫। এটি ম্যাট্রিক্সের আকার নির্দেশ করে (৫×৫)। const মানে এটি পরিবর্তন করা যাবে না।
স্লাইডিং উইন্ডো অ্যালগরিদম: বাংলায় বিস্তারিত গাইড (২০২৫)
স্লাইডিং উইন্ডো অ্যালগরিদম: বাংলায় বিস্তারিত গাইড
স্বাগতম! এই ব্লগে আমরা আলোচনা করবো স্লাইডিং উইন্ডো অ্যালগরিদম সম্পর্কে, যা প্রোগ্রামিং এবং অ্যালগরিদম সমাধানে একটি অত্যন্ত কার্যকরী টেকনিক। এটি কী, কীভাবে কাজ করে, কোথায় ব্যবহৃত হয়, এবং পাইথন ও সি++ এর একাধিক উদাহরণ সহ বিস্তারিতভাবে বাংলায় ব্যাখ্যা করা হবে।
স্লাইডিং উইন্ডো অ্যালগরিদম কী?
স্লাইডিং উইন্ডো হলো একটি অ্যালগরিদম টেকনিক যা একটি নির্দিষ্ট বা পরিবর্তনশীল আকারের "উইন্ডো" ব্যবহার করে ডেটা (যেমন অ্যারে বা স্ট্রিং) প্রক্রিয়া করে। এই উইন্ডোটি ডেটার উপর দিয়ে স্লাইড করে এবং প্রতিটি ধাপে নির্দিষ্ট কাজ সম্পন্ন করে। এটি সময় জটিলতা কমাতে অত্যন্ত কার্যকর।
এটি কীভাবে কাজ করে?
স্লাইডিং উইন্ডো টেকনিক প্রধানত দুই ধরনের হয়:
নির্দিষ্ট আকারের উইন্ডো (Fixed Size Window): উইন্ডোর আকার স্থির থাকে।
পরিবর্তনশীল আকারের উইন্ডো (Variable Size Window): উইন্ডোর আকার প্রয়োজন অনুযায়ী পরিবর্তিত হয়।
এটি ডেটার একটি অংশ প্রক্রিয়া করে এবং তারপর উইন্ডোটি সামনে সরিয়ে পরবর্তী অংশে যায়।
কোথায় ব্যবহৃত হয়?
স্লাইডিং উইন্ডো বিভিন্ন প্রোগ্রামিং সমস্যায় ব্যবহৃত হয়, যেমন:
সর্বাধিক বা সর্বনিম্ন উপ-অ্যারে খুঁজে বের করা।
নির্দিষ্ট যোগফলের উপ-অ্যারে খুঁজে পাওয়া।
স্ট্রিং-এ প্যাটার্ন ম্যাচিং।
ডেটা স্ট্রিম প্রক্রিয়াকরণ।
উদাহরণ ১: নির্দিষ্ট আকারের উইন্ডো (সর্বাধিক যোগফল)
ধরা যাক, আমাদের অ্যারে [1, -3, 2, 1, -1, 4] এবং আমরা ৩টি উপাদানের সর্বাধিক যোগফল চাই।
পাইথন কোড
arr = [1, -3, 2, 1, -1, 4]
k = 3 # উইন্ডোর আকার
max_sum = float('-inf')
for i in range(len(arr) - k + 1):
window_sum = sum(arr[i:i + k])
max_sum = max(max_sum, window_sum)
print(f"উইন্ডো {i+1}: {arr[i:i + k]}, যোগফল: {window_sum}")
print(f"সর্বাধিক যোগফল: {max_sum}")
সি++ কোড
#include
#include
using namespace std;
int main() {
vector arr = {1, -3, 2, 1, -1, 4};
int k = 3;
int max_sum = INT_MIN;
for (int i = 0; i <= arr.size() - k; i++) {
int window_sum = 0;
for (int j = i; j < i + k; j++) {
window_sum += arr[j];
}
max_sum = max(max_sum, window_sum);
cout << "উইন্ডো " << i + 1 << ": ";
for (int j = i; j < i + k; j++) cout << arr[j] << " ";
cout << ", যোগফল: " << window_sum << endl;
}
cout << "সর্বাধিক যোগফল: " << max_sum << endl;
return 0;
}
ধরা যাক, স্ট্রিং "abcabcbb" এবং আমরা দীর্ঘতম সাবস্ট্রিং চাই যেখানে কোনো অক্ষর পুনরাবৃত্তি নেই।
পাইথন কোড
s = "abcabcbb"
char_map = {}
start = 0
max_length = 0
max_substring = ""
for end in range(len(s)):
if s[end] in char_map and char_map[s[end]] >= start:
start = char_map[s[end]] + 1
else:
if end - start + 1 > max_length:
max_length = end - start + 1
max_substring = s[start:end + 1]
char_map[s[end]] = end
print(f"দীর্ঘতম সাবস্ট্রিং: {max_substring}, দৈর্ঘ্য: {max_length}")
সি++ কোড
#include
#include
#include
using namespace std;
int main() {
string s = "abcabcbb";
unordered_map char_map;
int start = 0, max_length = 0;
string max_substring;
for (int end = 0; end < s.length(); end++) {
if (char_map.find(s[end]) != char_map.end() && char_map[s[end]] >= start) {
start = char_map[s[end]] + 1;
} else if (end - start + 1 > max_length) {
max_length = end - start + 1;
max_substring = s.substr(start, max_length);
}
char_map[s[end]] = end;
}
cout << "দীর্ঘতম সাবস্ট্রিং: " << max_substring << ", দৈর্ঘ্য: " << max_length << endl;
return 0;
}
আউটপুট:
দীর্ঘতম সাবস্ট্রিং: abc, দৈর্ঘ্য: 3
সুবিধা ও অসুবিধা
সুবিধা:
সময় জটিলতা কমায় (O(n²) থেকে O(n))।
বাস্তবায়ন সহজ এবং বোঝা যায়।
অ্যারে ও স্ট্রিং উভয় ক্ষেত্রেই কার্যকর।
অসুবিধা:
সব সমস্যার জন্য উপযুক্ত নয়।
কিছু ক্ষেত্রে অতিরিক্ত মেমোরি লাগতে পারে।
উপসংহার
স্লাইডিং উইন্ডো অ্যালগরিদম প্রোগ্রামিং সমস্যা সমাধানের জন্য একটি শক্তিশালী এবং জনপ্রিয় টেকনিক। এই ব্লগে আমরা এটির বিস্তারিত ব্যাখ্যা, পাইথন ও সি++ এর উদাহরণ, এবং এর প্রয়োগ দেখেছি। এটি শিখে আপনি অনেক জটিল সমস্যা সহজে সমাধান করতে পারবেন।
স্লাইডিং উইন্ডো প্র্যাকটিস প্রবলেম
এখানে কিছু জনপ্রিয় প্রোগ্রামিং প্ল্যাটফর্ম থেকে স্লাইডিং উইন্ডো সম্পর্কিত সমস্যার লিঙ্ক দেওয়া হলো। এগুলো অনুশীলন করে আপনি আপনার দক্ষতা বাড়াতে পারেন।
প্রিফিক্স সাম হল একটি অ্যালগোরিদম, যা একটি অ্যারের উপাদানগুলোর যোগফল সংরক্ষণের একটি পদ্ধতি। এটি ব্যবহার করে, আমরা যেকোনো সাবঅ্যারে তে যোগফল বের করতে পারি অতি দ্রুত সময়ে।
প্রিফিক্স সাম কেন ব্যবহার করা হয়?
ধরি, আপনার কাছে একটি অ্যারে রয়েছে: [1, 2, 3, 4, 5]. এখন আপনি চাইতেছেন, arr[l] থেকে arr[r] পর্যন্ত যোগফল বের করতে। আপনি চাইলে অন্যভাবে খুব সহজেই করতে পারবেন কিন্তু অনলাইন জাজে Time Limit Exceed(TLE) খেতে পারেন। আর এই সমস্যা খুব সহজেই আপনি প্রিফিক্স সামের সাহায্যে সমাধান করতে পারবেন।
প্রিফিক্স সাম ব্যবহার না করলে আপনাকে প্রতি বার লুপ চালাতে হয়। কিন্তু যদি প্রিফিক্স সাম ব্যবহার করেন তাহলে একবার হিসাব করে, পরবর্তী সময়গুলোতে আপনি মাত্র O(1) সময়ে কাজটি করতে পারবেন।
প্রিফিক্স সামের সুবিধা :
প্রিফিক্স সামের সুবিধা হল যে এটি একবার অ্যারের উপাদানগুলোর যোগফল হিসাব করে সংরক্ষণ করে। যার পরবর্তী সময়ে সাবঅ্যারের যোগফল বের করার জন্য আমাদের সময় O(1) থাকে।
যেমনঃ, প্রিফিক্স সাম তৈরি করতে সময় লাগবে O(n), এবং সাবঅ্যারের যোগফল বের করতে O(1) সময় লাগে।
প্রিফিক্স সাম গণনার উদাহরণ :
C++ উদাহরণ
#include
using namespace std;
int main() {
int arr[] = {1, 2, 3, 4, 5};
int n = 5;
int prefix[n];
prefix[0] = arr[0]; // প্রথম উপাদান কপি করছি
for (int i = 1; i < n; i++) {
prefix[i] = prefix[i - 1] + arr[i]; // প্রতিটি উপাদান যোগ করতে থাকছি
}
cout << "Prefix Sum Array: ";
for (int i = 0; i < n; i++) {
cout << prefix[i] << " "; // আউটপুট: 1 3 6 10 15
}
return 0;
}
Python উদাহরণ
def prefix_sum(arr):
prefix = [0] * len(arr)
prefix[0] = arr[0] # প্রথম উপাদান কপি করছি
for i in range(1, len(arr)):
prefix[i] = prefix[i - 1] + arr[i] # প্রতিটি উপাদান যোগ করতে থাকছি
return prefix
arr = [1, 2, 3, 4, 5]
print("Prefix Sum Array:", prefix_sum(arr)) # আউটপুট: [1, 3, 6, 10, 15]
কিভাবে সাবঅ্যারের যোগফল বের করা যাবে?
যদি আপনি জানেন যে আপনার প্রিফিক্স সাম অ্যারে তৈরি হয়ে গেছে, তাহলে আপনার সাবঅ্যারের যোগফল O(1) সময়ে বের করা যাবে। এই কোডটি দেখুন:
C++ উদাহরণ
int rangeSum(int prefix[], int l, int r) {
if (l == 0) return prefix[r];
return prefix[r] - prefix[l - 1]; // সাবঅ্যারের যোগফল বের করা
}
Python উদাহরণ
def range_sum(prefix, l, r):
if l == 0:
return prefix[r]
return prefix[r] - prefix[l - 1] # সাবঅ্যারের যোগফল বের করা
প্রিফিক্স সামের সুবিধা ও প্রয়োগ
প্রিফিক্স সামের সবচেয়ে বড় সুবিধা হলো, আপনি একবার যোগফল হিসাব করে সংরক্ষণ করে ফেললে, পরবর্তীতে সাবঅ্যারের যোগফল বের করতে মাত্র O(1) সময়ে কাজ করতে পারবেন। এতে আপনার প্রোগ্রাম আরও দ্রুত কাজ করবে।
প্রিফিক্স সাম ব্যবহার করে আপনি বিভিন্ন ধরনের সমস্যা যেমন, সাবঅ্যারের যোগফল, সাবঅ্যারের গড় বের করা, এবং অন্যান্য সংখ্যাসূচক সমস্যা সমাধান করতে পারবেন।
Imagine going to a coffee shop, and the barista automatically gives you a cappuccino because you didn't specify your order. That’s what JavaScript ES6 Default Parameters do! 🎉
📌 Before ES6: The Struggle Was Real
Before ES6, we had to manually check if a function parameter was provided. If not, we assigned a default value like this:
জাভাস্ক্রিপ্ট ইএস ৬(ES6) এ আরেকটা চমৎকার কনসেপ্ট হলো
ডিফল্ট(Default) প্যারামিটার যা ফাংশনে ব্যাবহার করা হয় । কোনো ফাংশন
ডিফাইন করার সময় এর সাথে দেওয়া প্যারামিটারের একটা ডিফল্ট ভ্যালু সেট করে
দেওয়াটাই হইলো ডিফল্ট(Default) প্যারামিটার ।
যদিও জাভাস্ক্রিপ্ট ইএস৫ এ এই কনসেপ্ট ছিলো নাহ তারপরেও লজিক ব্যাবহার
করে ডিফল্ট প্যারামিটারের কাজটা সেরে ফেলা যেত। আমরা এখন ইএস৫ এবং ইএস৬
দুইটাতেই দেখবো যে - ডিফল্ট প্যারামিটার কনসেপ্ট আসার আগে কেমনে হ্যান্ডেল
করা হতো আর
আসার পর কেমনে করা হয় ।
const info = (Name, Town) => {
// do something
}
info();
ধরুন, আমাদের info নামে একটা ফাংশন আছে, যার দুইটা প্যারামিটার আছে Name
এবং Town নামে । যদি কেউ ফাংশন কল করার পর প্যারামিটার এর ভ্যালু পাস করে
তাহলে সেই অনুযায়ী রেজাল্ট দিবে আর যদি ভ্যালু পাস নাহ করা হয় তাহলে
ডিফল্টভাবে Name='Shakil Babu' এবং Town='Bogura' ধরে রেজাল্ট দিবে।
প্রথমে চলুন আমরা এইটা ইএস৫ এ কেমনে হ্যান্ডেল করে সেইটা দেখি -
const info = (Name, Town) => {
Name ? Name = Name : Name = 'Shakil Babu' ;
Town ? Town = Town : Town = 'Bogura'
console.log(`I am ${Name} from ${Town}`);
}
এখন কেউ যদি, ফাংশন কল করে প্যারামিটারের ভ্যালু পাস করে তাহলে সেই অনুযায়ী আমাদের আউটপুট দেখাবে :-
info('Torikus', 'Dinajpur');
// output :
// I am Torikus from Dinajpur
এবার যদি, প্যারামিটারের ভ্যালু পাস নাহ করা হয় :-
info();
// output :
// I am Shakil Babu from Bogura
দেখছেন ? আমাদের ডিফল্টভাবে সেট করে দেওয়া ভ্যালুগুলো নিয়েই কাজ করেছে।
এখন এই সেইম কাজটা ইএস৬ এর ডিফল্ট(Default) প্যারামিটার দিয়ে করা যাক -
const info = (Name='Shakil Babu', Town='Bogura') => {
console.log(`I am ${Name} from ${Town}`);
}
যদি কেউ প্যারামিটারের ভ্যালু পাস করে :-
info('Torikus', 'Dinajpur');
// output :
// I am Torikus from Dinajpur
এবার যদি, প্যারামিটারের ভ্যালু পাস নাহ করা হয় -
info();
// output :
// I am Shakil Babu from Bogura
আমরা কিন্তু সেইম কাজটা অনেক সহজ ওয়েতে ইএস৬ এর ডিফল্ট(Default)
প্যারামিটার দিয়ে করলাম । আর এইটাই স্ট্যান্ডার্ড ওয়ে যা আগে ডেভেলপাররা
চাইতো এবং ফাইনালি ইএস৬ সেইটা এনে দিয়েছে ।
এবার ছোট্ট একটা প্রব্লেমের সাথে ডিফল্ট(Default) প্যারামিটার ব্যাবহার করা যাক :-
প্রব্লেম - একটি নামতা প্রিন্ট করার প্রোগ্রাম লিখতে হবে যেখানে ফাংশন
প্যারামিটার হিসেবে একটি নাম্বার নিবে । যদি প্যারামিটারের ভ্যালু পাস করা
হয় তাহলে সেই ভ্যালু অনুযায়ী নয়তো ১ এর ঘরের নামতা প্রিন্ট করতে হবে ।
আপনারা কিন্তু, ইতিমধ্যে বুঝে ফেলছেন এইটা কিভাবে করতে হবে । এখানে অবশ্যই ডিফল্ট(Default) প্যারামিটারের ব্যাবহার করতে হবে।
তো চলুন দেখা যাক -
const multipicationTable = (number = 1) =>{
for (let i = 1; i <= 10 ; i++) {
console.log(`${number}*${i} = ${i*number}`);
}
}
এখন, উপরোক্ত ফাংশন কল করে আমরা একটা নাম্বার ভ্যালু পাস করি যেমন - 10 ।
স্ট্রিং ক্যারেক্টার ম্যাপ তৈরি করার সহজ উপায়: জাভাস্ক্রিপ্ট উদাহরণ
হে সুন্দর মানুষজন,
আজকে আমি ক্যারেক্টার ম্যাপ নিয়ে আলোচনা করবো, অর্থাৎ কিভাবে একটি
স্ট্রিং এর ক্যারেক্টার ম্যাপ তৈরি করতে হয় তা দেখবো। কোনো একটি স্ট্রিং এর
ক্যারেক্টার ম্যাপ তৈরি করতে পারলে খুব সহজেই স্ট্রিং রিলেটেড অনেক সমস্যা
সমাধান করা যায়। তো বেশি বক-বক নাহ করে জেনে নেওয়া যাক যে ক্যারেক্টার
ম্যাপ কী?
স্ট্রিং কাকে বলে ?
আমরা তো সবাই জানি যে স্ট্রিং কি বা কাকে বলে । একদম সহজ ভাবে বলতে গেলে
জাভাস্ক্রিপ্টে সিঙ্গেল বা ডাবল কোটেশানের মাঝে যাই থাকে তাকেই স্ট্রিং
বলে । স্ট্রিং মুলত কতগুলো ক্যারেক্টারের সমষ্টি ।
ক্যারেক্টার ম্যাপ কী ?
কোনো একটা স্ট্রিং এ কি কি ক্যারেক্টার আছে তার লিস্ট করা সাথে কোন ক্যারেক্টার কতোবার আছে তার হিসেব করা । যেমন ধরি, আমাদের কাছে "babu" নামে একটি স্ট্রিং আছে, তাহলে এর ক্যারেক্টার ম্যাপ হবে এইরকম :-
{
b: 2,
a: 1,
u: 1
}
আমরা দেখতে পাইতেছি যে, "babu" নামের এই স্ট্রিং টাতে কিন্তু b ক্যারেক্টার দুইটি তাই b : 2 এমনভাবে দেখাচ্ছে এবং a,u একটি করে তাই তাদের পরিমাণটাও একটি(1) করে দেখাচ্ছে।
ক্যারেক্টার ম্যাপ তৈরি করার সুবিধা কী ?
কোনো একটি স্ট্রিং এর ক্যারেক্টার ম্যাপ তৈরি করে খুব সহজেই বিভিন্ন স্ট্রিং রিলেটেড সমস্যা সমাধান করতে পারবেন যেমন -
১। স্ট্রিং এ সবচেয়ে কমন ক্যারেক্টার কোনটি বা কোনগুলো ?
২। স্ট্রিং টি অ্যানাগ্রাম কি নাহ ?
৩। স্ট্রিং এ কোনো রিপিটেড ক্যারেক্টার আছে কি নাহ ?
সবকিছু ঠিক-ঠাক । তবে একটা কথা বলে রাখা ভালো যে আপনি অনেক ভাবেই
ক্যারেক্টার ম্যাপ তৈরি করতে পারেন । আপনাকে যে উপরোক্ত ভাবেই ক্যারেক্টার
ম্যাপ তৈরি করতে হবে সেই রকম কোনো বাধা-ধরা নিয়ম নেই ।
জাভাস্ক্রিপ্ট ইএস৬(ES6) এ নতুন একটি লুপের সাথে পরিচয় করিয়ে দেওয়া হয়েছে যার নাম for...of লুপ । এই for...of লুপের সাহায্যে খুব সহজেই অ্যারে(array), সেট(set), ম্যাপ(map), স্ট্রিং(string) ইত্যাদিকে ইটারেট করা যায় ।
জাভাস্ক্রিপ্ট for...of লুপ সিনট্যাক্স :-
for (item of iterable) {
// body of for...of
}
এখানে,
iterable হলো যেকোনো একটি ইটারেবল(iterable) অবজেক্ট যেমন - অ্যারে(array), সেট(set), স্ট্রিং(string) ইত্যাদি ।
item হলো ইটারেবল(iterable) অবজেক্টের ভ্যালু ।
এখন আমরা for...of লুপের ইউসকেস দেখবো অর্থাৎ এটি কিভাবে কাজ করে -
for...of with Arrays
কোনো একটা অ্যারেকে ইটারেট করার প্রয়োজন হলে আমরা for...of ব্যাবহার করতে পারি যেমন :-
// array
const friends = ['shakil', 'torikus', 'fahim'];
// using for...of
for ( let item of friends ) {
// display the values
console.log(item);
}
OUTPUT :-
shakil
torikus
fahim
উপরের প্রোগ্রামে friends আরেকে ইটারেট করা হয়েছে অর্থাৎ অ্যারের ভেতর লুপ থ্রো করা হয়েছে যা অ্যারের লেন্থ পর্যন্ত চলবে । এবং item of friends এই লাইনে item নামে একটা ভ্যারিয়েবল নেওয়া হয়েছে যা friends আরের ভ্যালুকে নির্দেশ করে ।
প্রত্যেকবার লুপটি ইটারেট করার সময় কনসোলে item কে লগ করে যার ফলে
উপরোক্ত আউটপুট পেয়েছি । শেষে এটি অ্যারের লেন্থ পর্যন্ত চলে ইটেরেশনকে
বন্ধ করে দেয় ।
for...of with Strings :-
for...of লুপ আমরা স্ট্রিং(String) কে ইটারেট করতেও ব্যাবহার করতে পারি যেমন :-
// string
const name = 'babu';
// using for...of loop
for (let i of name) {
console.log(i);
}
OUTPUT :-
b
a
b
u
অ্যারের মতো সেইমভাবে স্ট্রিং ও ইটারেট করা হয়েছে । এখানে i স্ট্রিং এর প্রত্যেকটা ভালুকে নির্দেশ করে যার ফলে আমরা উপরোক্ত আউটপুট পেয়েছি ।
for...of with Sets :-
আমরা Set এর এলিমেন্টস গুলোকে ইটারেট করতে পারি সেইম ভাবে for...of লুপ ইউস করে যেমন :-
// define Set
const set = new Set([1, 2, 3]);
// looping through Set
for (let i of set) {
console.log(i);
}
OUTPUT :-
1
2
3
সেইম ভাবে এখানে iSet এর প্রত্যেকটা এলিমেন্ট বা ভ্যালুকে নির্দেশ করে যার প্রমাণসরূপ প্রোগ্রামের আউটপুট ।
for...of with Maps:-
আমরা সেইমভাবে Map এলিমেন্টস ইটারেট করতে পারি for...of লুপ ইউস করে যেমন :-
// define Map
let map = new Map();
// inserting elements
map.set('name', 'Jack');
map.set('age', '27');
// looping through Map
for (let [key, value] of map) {
console.log(key + '- ' + value);
}
OUTPUT :-
name- Jack
age- 27
আশা করি বুঝতে পারছেন ।
======== শেষের কথা ========
for...of লুপ ইউস করা হয় শুধুমাত্র ইটারেবল(iterable) অব্জেক্টসের ভ্যালুসমূহ কে ইটারেট করতে ।
যদি কোনো ইটারেবল(iterable) অব্জেক্টসের ইনডেক্স নিয়ে কাজ করার দরকার পরে তাহলে এই লুপ ইউস না করায় ভালো ।
for...of লুপ অবজেক্টের(object) সাথে ইউস করা যায় নাহ ।
এই সর্টিং অ্যালগরিদমের মূল বিষয় হলো - একটি নির্দিষ্ট সংখ্যার জন্য এমন
একটি জায়গা খুঁজে বের করা যেখানে সংখ্যাটিকে রাখলে তার আগের সংখ্যাটি তার
থেকে ছোট বা সমান হবে এবং পরের সংখ্যাটি তার থেকে বড় হবে । তবে সংখ্যাটি
যদি অ্যাঁরে বা লিস্টের সবচেয়ে ছোট বা বড় সংখ্যা হয় তাহলে তার জন্য সবার
প্রথমে জায়গা করে দিতে হবে ।যেমন : - নিচের visualization টি লক্ষ্য করি :-
ধরি, আমাদের কাছে [10,30,40] এই অ্যাঁরেটি ছোট থেকে বড়
ক্রমে সাজানো আছে । আমি এই অ্যাঁরেতে আরেকটি সংখ্যা যুক্ত করতে চাই
সংখ্যাটি হলো 20 । এখন আমাকে 20 কে এমন এক জায়গায় বসাতে হবে যেখানে তার
আগের সংখ্যা তার থেকে ছোট বা সমান হবে এবং তার পরের সংখ্যাটি তার থেকে বড়
হবে ।
যদি অ্যাঁরেতে লক্ষ করি তাহলে দেখতে পারবো যে, সেই কাঙ্খিত জায়গাটি হচ্ছে 10
এর পরের অবস্থান কারণ, 20 থেকে 10 ছোট এবং 30 বড় । এখন যেহেতু 10 এর পরের
অবস্থানে 30 আছে, তাহলে 30 এর জায়গায় 20 কে বসাতে হলে অ্যাঁরের শেষ থেকে
অর্থাৎ 40 কে এক ঘর ডানে সরাতে হবে এখন 40 যেখানে ছিলো সেই ঘরটি কিন্তু
ফাকা -
[10, 30, _, 40]
এরপর 30 কে এক ঘর ডানে সরাই - [10, _, 30, 40]
এখন ফাকা জায়গাটি কিন্তু 20 এর জন্য পারফেক্ট জায়গা অর্থাৎ, যে স্থানটি
ফাকা তার আগের সংখ্যাটি 20 থেকে ছোট এবং পরের সংখ্যাটি 20 থেকে বড় । তাহলে
ফাকা জায়গাটিতে 20 কে এসাইন করি -
[10, 20, 30, 40]
এতক্ষণ যে পদ্ধতিতে একটি সংখ্যা অ্যাঁরেতে সাজানো হলো এই পদ্ধতিকেই আমরা ইনসারশন সর্ট(Insertion sort) বলতে পারি ।
অ্যারে(Array) ও ইনসার্শন সর্ট(Insertion sort) -
তো চলুন এখন একটি এলোমেলো অ্যাঁরে বা লিস্টকে ইনসারশন সর্ট(Insertion
sort) পদ্ধতিতে ছোট থেকে বড় ক্রমে বা Accending order এ সাজাই । ধরি, আমার
কাছে একটি অ্যারে আছে -
[50, 20, 10, 30]
স্টেপ(০১) :- প্রথমে 50 এবং 20 এর মধ্যে ছোট সংখ্যার জন্য
উপযুক্ত জায়গা খুঁজে বের করবো । এক্ষেত্রে, 20 কে 50 এর অবস্থানে বসাতে হবে
তাই 50 কে এক ঘর ডানদিকে সরাই । এখন কিন্তু 50 এর জায়গা খালি তাই খালি
জায়গায় 20 কে এসাইন করি -
[20, 50, 10, 30]
স্টেপ(০২) :- 50 এবং 10 এর মধ্যে 10 এর জন্য উপযুক্ত জায়গা
খুঁজে বের করি । এক্ষেত্রে, অ্যারের প্রথমে 10 কে রাখতে হবে অর্থাৎ 20 এর
অবস্থানে । তাই, প্রথমে 50 কে এক ঘর ডান দিকে সরাই - [20, _, 50, 30]
এখনও উক্ত ফাকা স্থানটি কিন্তু 10 এর উপযুক্ত জায়গা না তাই 20 কেও এক ঘর ডান দিকে সরাই - [_, 20, 50, 30]
এখন ফাকা স্থানে অর্থাৎ 20 এর আগের ঘরে 10 এসাইন করি -
[10, 20, 50, 30]
স্টেপ(০৩) :- 50 এবং 30 এর মধ্যে 30 এর জন্য উপযুক্ত জায়গা খুঁজে বের করি এক্ষেত্রে, 20 এবং 50 এর মাঝে । তাই 50 কে এক ঘর ডানদিকে সরাই -
[10, 20, _, 50]
এখন ফাকা জায়গাটিতে 30 কে এসাইন করি -
[10, 20, 30, 50]
ব্যাস, উপরোক্ত অ্যারে বা লিস্টটি ছোট থেকে বড় ক্রমে অর্থাৎ Accending
order এ সাজানো হয়ে গেলো । এখন আপনি যদি বুঝতে না পারেন তাহলে খাতা-কলম
নিয়ে বিষয়টি বোঝার চেষ্টা করতে পারেন । যদি প্রথম বা দ্বিতীয় বারে বিষয়টি
বুঝে থাকেন তাহলে আপনি একটি বড় অ্যারে বা লিস্টকে খাতা-কলমে সর্ট করতে
পারেন । তো চলুন এখন ইমপ্লিমেন্টেশন করা যাক -
In Python :
def insertionSort(arr):
for i in range(1, len(arr)):
# arr[i] কে currentValue তে এসাইন করি
currentValue = arr[i]
# currentValue এর জন্য উপযুক্ত স্থান খুঁজে বের করিj = i - 1# যদি j, 0 থেকে সমান বা বড় হয় এবং arr[j] থেকে currentValue ছোট হয়
while j >= 0and arr[j] > currentValue:
# arr[j] কে তার পরের ঘরে রেখে দিই
arr[j+1] = arr[j]
# j এর মান 1 করে কমাইj -= 1# এখন খালি জায়গায় currentValue কে বসাই
arr[j+1] = currentValue
return arr
In JavaScript :
function insertionSort(arr) {
// একটি currentValue নামে ভেরিয়েবল নিই
var currentValue;
// i এর মান 1 থেকে len(arr) এর আগ পর্যন্ত 1 করে বাড়াই
for (var i = 1; i < arr.length; i++) {
// arr[i] কে currentValue তে এসাইন করি
currentValue = arr[i];
// currentValue এর জন্য উপযুক্ত স্থান খুঁজে বের করি (j = i - 1)
// যদি j, 0 থেকে সমান বা বড় হয় এবং arr[j] থেকে currentValue ছোট হয়
for (var j = i - 1; j >= 0 && arr[j] > currentValue; j--) {
// arr[j] কে তার পরের ঘরে রেখে দিই
arr[j + 1] = arr[j];
}
// এখন খালি জায়গায় currentValue কে বসাই
arr[j + 1] = currentValue;
}
return arr;
}
আপনি চাইলে জাভাস্ক্রিপ্ট কোডে for লুপের পরিবর্তে while লুপ ব্যাবহার করতে পারেন ।
উপরোক্ত কোড বিশ্লেষণ :-
উপরের প্রোগ্রামে, প্রথমে i=1 থেকে অ্যারের লেন্থ পর্যন্ত
লুপ চালিয়েছি । তারপর currentValue নামে একটি ভেরিয়েবলের মাঝে অ্যারের
Present ভ্যালুকে রাখছি । এক্ষেত্রে কারেন্ট ভ্যালুটি যাতে হারিয়ে না যায়
তা নিশ্চিত করছি কারণ পরবর্তীতে এই currentValue কেই তার যথাযথ স্থানে
রাখতে হবে ।
তারপর ভেতরের লুপে j >= 0 এবং arr[j] > currentValue
এইভাবে চেক করতেছি । এখানে, যদি j এর মান 0 থেকে ছোট হয় তাহলে আমি বুঝতে
পারবো যে অ্যারে বা লিস্টের সব উপাদানই currentValue থেকে বড় । আবার, j এর
মান যদি 0 থেকে বড় বা সমান হয় তাহলে দ্বিতীয় শর্তপরীক্ষা করা করবো অর্থাৎ
arr[j] > currentValue ।
এখন যদি দ্বিতীয় শর্ত অর্থাৎ arr[j] > currentValue সত্য হয় তাহলে, arr[j] কে আমরা এক ঘর ডানে সরিয়ে দিব - arr[j + 1] = arr[j]
আবার, arr[j] > currentValue যদি মিথ্যা হয় তাহলে, লুপ থেকে বের হয়ে
arr[j + 1] এ currentVal কে এসাইন করে দিব - arr[j + 1] = currentVal
ইত্যাদি ।
যেকোনো অ্যালগরিদম বাঁ ডাটা-স্ট্রাকচারের মূল কনসেপ্ট যদি আপনি ভালোভাবে
বুঝতে পারেন তাহলে যেকোনো প্রোগ্রামিং ল্যাঙ্গুয়েজ ব্যাবহার করেই
ইমপ্লিমেন্ট করতে পারবেন । চলুন ইনসার্শন সর্ট(Insertion sort)
অ্যালগরিদমের টাইম ও স্পেস কমপ্লেক্সিটি বের করা যাক -
টাইম কমপ্লেক্সিটি(Time Complexity) :
উপরোক্ত কোড লক্ষ করলে আশা করি টাইম কমপ্লেক্সিটি(Time Complexity) বলে
দিতে পারবেন । এই অ্যালগোরিদমেরও টাইম কমপ্লেক্সিটি(Time Complexity) O(n2) ।
স্পেস কমপ্লেক্সিটি(Space Complexity) :
উপরের প্রোগ্রামে একটু খেয়াল করলে দেখতে পারবো যে, প্রোগ্রামে একটি
লিস্ট বা অ্যারে প্যারামিটার হিসেবে নেওয়া হয়েছে । এবং যা কাজ-কারবার করা
হয়েছে তা এই অ্যারে বা লিস্টের মধ্যেই । এই অ্যারে বা লিস্টে কিন্তু কোনো
নতুন উপাদান সংযোজন, বিয়োজন ইত্যাদি কিছুই করা হয় নাই । তাহলে বলতে পারি
অ্যারে বা লিস্টটি Constant । এখন আপনার কাছে আমার প্রশ্ন এই প্রোগ্রামের
স্পেস কমপ্লেক্সিটি(Space Complexity) কত ?
ব্রাভো, যদি এতদূর পর্যন্ত আপনি ঠিকঠাক ভাবে পড়ে থাকেন তাহলে আমি ধরে
নিতেই পারি ইনসার্শন সর্ট(Insertion sort) অ্যালগরিদম সম্পর্কে ভালো একটা
দখল চলে আসছে ।
যদি সত্যি বুঝে থাকেন তাহলে একটি কাজ করতে পারেন, উপরোক্ত অ্যারেটিকে
আমি কিন্তু Accending Order অর্থাৎ ছোট থেকে বড় ক্রমে সাজিয়েছি এখন আপনি
ইনসার্শন সর্ট(Insertion sort) অ্যালগরিদম ব্যাবহার করে অ্যারেটিকে
Decending Order অর্থাৎ বড় থেকে ছোট ক্রমে সাজানোর চেষ্টা করবেন ।
খুবই সহজ একটি কাজ আশা করি পারবেন শুভকামনা রইলো ।
বাবল সর্ট অ্যালগরিদম (Bubble Sort Algorithm) - সহজ বর্ণনা ও ইমপ্লিমেন্টেশনবাবল সর্ট অ্যালগরিদম (Bubble Sort Algorithm) - সহজ বর্ণনা ও ইমপ্লিমেন্টেশনবাবল সর্ট অ্যালগরিদম (Bubble Sort Algorithm) - সহজ বর্ণনা ও ইমপ্লিমেন্টেশন
গত পর্বে আমি সিলেকশন সর্ট(Selection sort) অ্যালগরিদম নিয়ে আলোচনা
করেছিলাম এই পর্বে আরও একটি সহজ সর্টিং অ্যালগরিদম নিয়ে আলোচনা করবো নাম
বাবল সর্ট(Bubble sort) ।
কম্পিউটার সায়েন্সে যতগুলো সর্টিং অ্যালগরিদম রয়েছে তার-মধ্যে সবচেয়ে
সহজ অ্যালগরিদম বলা হয় বাবল সর্ট কে । তো চলুন আজাইরা ইন্ট্রো গল্প বাদ
দিয়ে বাবল সর্ট(Bubble sort) অ্যালগরিদম নিয়ে আলোচনা করা যাক ।
বাবল সর্ট(Bubble sort) অ্যালগরিদম :-
এই অ্যালগোরিদমের মূল বিষয় হলো, পরপর দুইটি উপাদানের মধ্যে তুলনা করে তাদের ক্রম অনুসারে রাখা । যেমন : - নিচের visualization টি লক্ষ্য করি :-
ধরি, আমার কাছে একটি অ্যারে আছে [400,200,100,300] । এখন এই অ্যারেটি
আমি বাবল সর্ট(Bubble sort) অ্যালগরিদম ব্যাবহার করে ছোট থেকে বড় ক্রমে
অর্থাৎ Ascending Order এ সাজাতে চাই ।
উপরোক্ত অ্যারেতে মোট ৪টি উপাদান বাঁ সংখ্যা আছে । আপনি চাইলে যেকোনো
সাইজের অ্যারে নিয়ে কাজ করতে পারবেন আমার সময় বাঁচানোর জন্য আমি কম সংখ্যক
উপাদানসুমহের অ্যারেটি নিলাম । চলুন কাজে আসা যাক -
স্টেপ(০১) :- প্রথমে আমি অ্যারের প্রথম ২টি উপাদান নিবো সেক্ষেত্রে
উপাদানগুল হলো 400 ও 200 । এখন এই ২টি সংখ্যার মধ্যে আমি তুলনা করবো যে
সংখ্যাটি ছোট তাকে প্রথমে এবং বড় সংখ্যাটিকে তার পরে রাখবো । যেহেতু, 200
ছোট তাই 200 কে প্রথমে এবং 400 কে তার পরে রাখলাম এখন অ্যারেটি হবে -
[200,400,100,300]
স্টেপ(০২) :- এবার 400 এবং 100 এর তুলনা করবো সেক্ষেত্রে ছোট সংখ্যাটি
100 এবং বড় সংখ্যাটি 400 । তাহলে 100 কে আগে এবং 400 কে পরে রাখলাম -
[200,100,400,300]
স্টেপ(০৩) :- এবারে 400 এবং 300 এর মধ্যে তুলনা করি । 400 > 300
এক্ষেত্রে 300 ছোট এবং 400 বড় তাহলে 300 কে প্রথমে 400 কে পরে রাখি -
[200,100,300,400]
এখন অ্যারেটি যদি লক্ষ করি, সবচেয়ে বড় সংখ্যাটি কিন্তু সবার শেষে চলে
এসেছে । এখন এই শেষ উপাদান বাঁ সংখ্যাটি বাদে বাকি ৩টি সংখ্যার মধ্যে
পুনরায় আগের মতো তুলনা করতে হবে ।
স্টেপ(০৪) :- 200 এবং 100 এর মধ্যে তুলনা করি এক্ষেত্রে 100 ছোট এবং 200
বড় সংখ্যা । তাহলে 100 কে প্রথমে এবং 200 কে তার পরের স্থানে রাখি -
[100,200,300,400]
স্টেপ(০৫):- 200 ও 300 এর মধ্যে যদি তুলনা করি তাহলে 200 হবে ছোট এবং
300 হবে বড় সংখ্যা । এখন এদের স্থানের অদল-বদল করতে হবে অর্থাৎ 200 কে আগে
এবং 300 কে পরের স্থানে রাখতে হবে কিন্তু অ্যারের দিকে লক্ষ করলে দেখতে
পারবো যে 200 এবং 300 তাদের যথাযথ স্থানেই রয়েছে ।
[100,200,300,400]
স্টেপ(০৬) :- 100 ও 200 এর মাঝে তুলনা করি স্পষ্ট দেখতে পাচ্ছি 100 ও
200 তাদের যথাযথ স্থানেই রয়েছে । সাথে বাকি সংখ্যাগুলোও কিন্তু ঠিকঠাক
স্থানেই রয়েছে । এ পর্যায়ে এসে বলতে পারি আমাদের অ্যারেটি সর্টেড অর্থাৎ
ছোট থেকে বড় ক্রমে সাজানো আছে ।
আশা করি, আপনারা বাবল সর্ট(Bubble sort) অ্যালগরিদম বুঝতে পেরেছেন যদি
একবারে বুঝে না থাকেন তাহলে আরেকবার পড়তে হবে । চলুন এখন ইমপ্লিমেন্টেশন
বাঁ কোড লিখা যাক -
In Python :-
def bubbleSort(arr):
# i এর মান 0 থেকে len(arr) এর আগ পর্যন্ত ১ করে বাড়াই
for i in range(len(arr)):
# j এর মান 0 থেকে len(arr)-i-1 এর আগ পর্যন্ত ১ করে বাড়াই
for j in range(0, len(arr) - i - 1):
# arr[j] > arr[j+1] সত্য হয় তাহলে দুইটিকে অদল-বদল করি
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
return arr
In JavaScript :-
const optimizedBubbleSort = (arr) => {
// i এর মান 0 থেকে arr.length এর আগ পর্যন্ত ১ করে বাড়াই
for (let i = 0; i < arr.length; i++) {
// j এর মান 0 থেকে arr.length-i-1 এর আগ পর্যন্ত ১ করে বাড়াই
for (let j = 0; j < arr.length - i - 1; j++) {
// arr[j] > arr[j+1] সত্য হয় তাহলে দুইটিকে অদল-বদল করি
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
return arr;
};
যেকোনো অ্যালগরিদম বাঁ ডেটা-স্ট্রাকচারের মূল কনসেপ্ট যদি আপনি ভালোভাবে
বুঝতে পারেন তাহলে যেকোনো প্রোগ্রামিং ল্যাঙ্গুয়েজ ব্যাবহার করেই
ইমপ্লিমেন্ট করতে পারবেন ।
চলুন বাবল সর্ট(Bubble sort) অ্যালগরিদমের টাইম ও স্পেস কমপ্লেক্সিটি বের করা যাক -
টাইম কমপ্লেক্সিটি(Time Complexity) :
উপরোক্ত কোড লক্ষ করলে দেখবো প্রথম লুপটি চলে i = 0 থেকে n পর্যন্ত । এবং ভেতরের অর্থাৎ ২য় লুপটি চলে j = 0 থেকে n-i-1 পর্যন্ত ।
যখন i = 0, তখন ভেতরের লুপটি চলে j = 0 থেকে j = n - i - 1 অর্থাৎ 3 বার ।
আবার i = 1 হলে, ভেতরের লুপটি চলে j = 1 থেকে j = n - i - 1 অর্থাৎ 2 বার ।
এভাবেই যতক্ষণ না লুপটি শেষ হয় চলতেই থাকবে ।
এখন আমরা খুব সহজেই বলে দিতে পারি যে, উক্ত প্রোগ্রামের টাইম কমপ্লেক্সিটি(Time Complexity) O(n*(n-i-1))
অর্থাৎ O(n*(n-1)) ।
=> n*(n - 1)
=> n2 - n
=> O(n2 - n)
O(n2 - n) কে আমরা O(n2) লিখতে পারি কারণ n2 এর তুলনায় n ছোট ।
স্পেস কমপ্লেক্সিটি(Space Complexity) :
উপরের প্রোগ্রামে একটু খেয়াল করলে দেখতে পারবো যে, প্রোগ্রামে একটি
লিস্ট বা অ্যারে প্যারামিটার হিসেবে নেওয়া হয়েছে । এবং যা কাজ-কারবার করা
হয়েছে তা এই অ্যারে বা লিস্টের মধ্যেই । এই অ্যারে বা লিস্টে কিন্তু কোনো
নতুন উপাদান সংযোজন, বিয়োজন ইত্যাদি কিছুই করা হয় নাই । তাহলে বলতে পারি
অ্যারে বা লিস্টটি Constant ।
এখন আপনার কাছে আমার প্রশ্ন এই প্রোগ্রামের স্পেস কমপ্লেক্সিটি(Space
Complexity) কত ?
ব্রাভো, যদি এতদূর পর্যন্ত আপনি ঠিকঠাক ভাবে পড়ে থাকেন তাহলে আমি ধরে
নিতেই পারি বাবল সর্ট(Bubble sort) অ্যালগরিদম সম্পর্কে ভালো একটা দখল চলে
আসছে ।
যদি সত্যি বুঝে থাকেন তাহলে একটি কাজ করতে পারেন, উপরোক্ত অ্যারেটিকে
আমি কিন্তু Ascending Order অর্থাৎ ছোট থেকে বড় ক্রমে সাজিয়েছি এখন আপনি
বাবল সর্ট(Bubble sort) অ্যালগরিদম ব্যাবহার করে অ্যারেটিকে Descending
Order অর্থাৎ বড় থেকে ছোট ক্রমে সাজানোর চেষ্টা করবেন ।
বড় থেকে ছোট ক্রমে সাজানোর প্রক্রিয়াকে বলা হয় Descending Order । যেমন :-
ফাইনাল পরীক্ষা শেষে রেজাল্ট শিটে শিক্ষার্থীদের নামগুলো মোট নম্বর অনুযায়ী বড় থেকে ছোট ক্রমে সাজানো থাকে ।
আসল কথায় আসা যাক, প্রোগ্রামিং করার সময় বিভিন্ন কাজে ডাটাসমূহকে এরকম
সর্ট করার প্রয়োজন হয় । তখন আমরা খুব সহজেই বিভিন্ন সর্টিং অ্যালগরিদম
ব্যাবহার করে ডাটাসমূকে সর্ট করে ফেলতে পারি । কম্পিউটার সায়েন্সে অনেক
ধরণের সর্টিং অ্যালগরিদম আছে তারমধ্যে সহজ এবং জনপ্রিয় একটি সর্টিং
অ্যালগরিদম হইলো সিলেকশন সর্ট(Selection sort) ।
সিলেকশন সর্ট(Selection sort) :-
এই সর্টিং অ্যালগরিদমের মূল বিষয় হলো, প্রতি ইটারেশনে ডাটাসমূহ থেকে
একটি সঠিক উপাদানকে সিলেক্ট করা এবং তাকে অন্য আরেকটি উপাদানের সাথে
অদল-বদল করে(যদি প্রয়োজন হয়) সঠিক জায়গায় এসাইন করা ।
নিচে সিলেকশন সর্ট(Selection sort) এর Visualization লক্ষ্য করুন :-
ধরি, আমাদের কাছে [100,400,300,600,500] এই অ্যারেটি আছে । এখন এই
অ্যারেটিকে সিলেকশন সর্ট(Selection sort) অ্যালগরিদম ব্যাবহার করে
Descending Order অর্থাৎ বড় থেকে ছোট ক্রমে সাজাতে হবে।
স্টেপ(১) :- প্রথমে উপরোক্ত অ্যারে থেকে সবচেয়ে বড় সংখ্যাটিকে সিলেক্ট
করি এক্ষেত্রে 600 হচ্ছে সবচেয়ে বড় সংখ্যা । এখন 600 কে সবার প্রথমে নিয়ে
আসতে হবে এজন্য অ্যারের প্রথম উপাদান 100 এর সঙ্গে 600 এর স্থান অদল-বদল
করি । অদল-বদল শেষে আমাদের অ্যারেটি দাঁড়াবে এরকম -
[600,400,300,100,500]
স্টেপ(২) :- এখন অ্যারের দিকে লক্ষ করলে দেখতে পারবো যে, অ্যারের প্রথম
উপাদানটি সবচেয়ে বড় । তাই প্রথম উপাদান বাদে অ্যারের বাকি উপাদানগুলোর
মধ্যে সবচেয়ে বড় সংখ্যাটি সিলেক্ট করি এক্ষেত্রে সংখ্যাটি হচ্ছে 500 । এরপর
400 এর সাথে 500 এর স্থান অদল-বদল করি তাহলে অ্যারেটি দাঁড়াবে -
[600,500,300,100,400]
স্টেপ(৩) :- অ্যারের প্রথম দুটি উপাদান সর্ট করা শেষ এখন বাকি
তিনটি(300,100,400) উপাদান সর্ট করতে হবে । তাহলে এই তিনটি উপাদানের মধ্যে
সবচেয়ে বড় সংখ্যাটি সিলেক্ট করি সংখ্যাটি হলো 400 । এরপর 300 এর সাথে 400
এর স্থান অদল-বদল করি এখন অ্যারেটি হবে -
[600,500,400,100,300]
স্টেপ(৪) :- এখন আমরা নিশ্চিত যে, অ্যারের প্রথম ৩টি উপাদান সর্টেড
রয়েছে তাহলে বাকি ২টি(100,300) উপাদান থেকে সবচেয়ে বড় সংখ্যাটি সিলেক্ট করি
এক্ষেত্রে সংখ্যাটি হচ্ছে 300 । এখন 100 এর সাথে 300 এর স্থান অদল-বদল করি
। অদল-বদল শেষে অ্যারেটি হবে -
[600,500,400,300,100]
যেহেতু, অ্যারের ৫টি উপাদানের মধ্যে প্রথম ৪টি উপাদান বড় থেকে ছোট ক্রমে
সাজানো । সেক্ষেত্রে আমরা নিশ্চিত যে, অ্যারের শেষ উপাদান অর্থাৎ 100 তার
সঠিক জায়গাতেই আছে এর কোনো অদল-বদল করতে হবে নাহ । তাহলে আমাদের ফাইনাল
অর্থাৎ সর্টেড অ্যারেটি হলো -
[600,500,400,300,100]
আশা করি, আপনারা সিলেকশন সর্ট(Selection sort) অ্যালগরিদমটি বুঝতে
পেরেছেন যদি একবারে বুঝে না থাকেন তাহলে আরেকবার পড়তে হবে । চলুন এখন
ইমপ্লিমেন্টেশন বাঁ কোড লিখা যাক -
পাইথনে ইমপ্লিমেন্টেশন :-
def selectionSort(arr):
# i এর মান 0 থেকে len(arr) - 1 এর আগ পর্যন্ত ১ করে বাড়াই
for i in range(len(arr) - 1):
# largest নামে variable নিই এবং i এসাইন করি
largest = i
# j এর মান i+1 থেকে len(arr) এর পর্যন্ত ১ করে বাড়াই
for j in range(i+1, len(arr)):
# arr[largest] < arr[j] যদি সত্য হয় তাহলে largest এসাইন করি
if arr[largest] < arr[j]:
largest = j
# যদি largest এবং i সমান না হয় তাহলে সংখ্যা দুইটি অদল-বদল করি
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
return arr
জাভাস্ক্রিপ্টে ইমপ্লিমেন্টেশন :-
const selectionSort = (arr) => {
// i এর মান 0 থেকে arr.length - 1 এর আগ পর্যন্ত ১ করে বাড়াই
for (let i = 0; i < arr.length - 1; i++) {
// largest নামে variable নিই এবং i এসাইন করি
let largest = i;
// j এর মান i+1 থেকে arr.length এর পর্যন্ত ১ করে বাড়াই
for (let j = i + 1; j < arr.length; j++) {
// arr[largest] < arr[j] যদি true হয় তাহলে largest এ j এসাইন করি
if (arr[largest] < arr[j]) {
largest = j;
}
}
// যদি largest এবং i সমান না হয় তাহলে সংখ্যা দুইটি অদল-বদল করি
if (largest !== i) {
[arr[i], arr[largest]] = [arr[largest], arr[i]];
}
}
return arr;
};
যেকোনো অ্যালগরিদম বাঁ ডাটা-স্ট্রাকচারের মূল কনসেপ্ট যদি আপনি ভালোভাবে
বুঝতে পারেন তাহলে যেকোনো প্রোগ্রামিং ল্যাঙ্গুয়েজ ব্যাবহার করেই
ইমপ্লিমেন্ট করতে পারবেন ।
চলুন সিলেকশন সর্ট(Selection sort) অ্যালগরিদমের টাইম ও স্পেস কমপ্লেক্সিটি নিয়ে একটু অ্যানালাইসিস করা যাক -
টাইম কমপ্লেক্সিটি(Time Complexity) :
উপরোক্ত কোড লক্ষ করলে দেখবো প্রথম লুপটি চলে i = 0 থেকে n-1 পর্যন্ত । এবং ভেতরের অর্থাৎ ২য় লুপটি চলে j = i+1 থেকে n-1 পর্যন্ত ।
যখন i = 0, তখন ভেতরের লুপটি চলে j = 0 থেকে j = n - 1 অর্থাৎ 4 বার ।
আবার i = 1 হলে, ভেতরের লুপটি চলে j = 1 থেকে j = n - 1 অর্থাৎ 3 বার ।
i = 2 হলে, ভেতরের লুপটি চলে j = 2 থেকে j = n - 1 অর্থাৎ 2 বার ।
এভাবেই যতক্ষণ না লুপটি শেষ হয় চলতেই থাকবে ।
একটু লক্ষ করলে দেখতে পারবো যে, প্রথম লুপটি n সংখ্যক বার চললে তার
ভেতরের লুপটি চলতেছে m সংখ্যক বার । তাহলে টাইম কমপ্লেক্সিটি(Time
Complexity) দাঁড়াবে O(n * m) । এখানে যেহেতু m এর মান n এর সমান তাহলে
টাইম কমপ্লেক্সিটি(Time Complexity) বলতে পারি O(n * n) বা O(n2) ।
স্পেস কমপ্লেক্সিটি(Space Complexity) :
উপরের প্রোগ্রামে একটু খেয়াল করলে দেখতে পারবো যে, প্রোগ্রামে একটি
লিস্ট বা অ্যারে প্যারামিটার হিসেবে নেওয়া হয়েছে । এবং যা কাজ-কারবার করা
হয়েছে তা এই অ্যারে বা লিস্টের মধ্যেই । এই অ্যারে বা লিস্টে কিন্তু কোনো
নতুন উপাদান সংযোজন, বিয়োজন ইত্যাদি কিছুই করা হয় নাই । তাহলে বলতে পারি
অ্যারে বা লিস্টটি Constant ।
এখন আপনার কাছে আমার প্রশ্ন এই প্রোগ্রামের স্পেস কমপ্লেক্সিটি(Space
Complexity) কত ?
ব্রাভো, যদি এতদূর পর্যন্ত আপনি ঠিকঠাক ভাবে পড়ে থাকেন তাহলে আমি ধরে
নিতেই পারি সিলেকশন সর্ট(Selection sort) সম্পর্কে ভালো একটা দখল চলে আসছে ।
যদি সত্যি বুঝে থাকেন তাহলে একটি কাজ করতে পারেন, উপরোক্ত অ্যারেটিকে
আমি কিন্তু Descending Order অর্থাৎ বড় থেকে ছোট ক্রমে সাজিয়েছি এখন আপনি
সিলেকশন সর্ট(Selection sort) অ্যালগরিদম ব্যাবহার করে অ্যারেটিকে
Ascending Order অর্থাৎ ছোট থেকে বড় ক্রমে সাজানোর চেষ্টা করবেন ।