বিটওয়াইজ সিভ(Bitwise Sieve)
আমরা যদি int টাইপের একটি 0 নেই তাহলে এতে থাকবে 32 টি শূন্য। যেহেতু int চার বাইট এবং এক বাইট সমান আঁট বিট, তাহলে 4 X 8 = 32 বিট।
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 = 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 = 3
আবার ষষ্ঠ বিট 0 রেখে সপ্তম বিট 1 করে দিলে,
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 1 1 = 83
আবার অষ্টম বিট কে 0 রেখে নবম বিট কে 1 করে দিলে,
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 1 1 = 339
উপরের বিষয় টি যদি আপনি ধরতে পারেন তাহলে আপনি বিটওয়াইজ সিভ প্রায় শিখে ফেলেছেন। এখন, সামনে যা দেখব তার জন্য দুটি বিষয় জেনে রাখা আবশ্যক, বিটওয়াইজ অপারেটর ও ইরাটোস থেনিসের সিভ(Sieve of Eratosthenes)।
এখন আসি আসল বিষয়ে। আমরা সাধারণ সিভ অ্যালগরিদমে মার্ক করার জন্য int বা bool টাইপের ডাটা নিয়ে কাজ করি। একটি নাম্বার প্রাইম কিনা তা জানার জন্য আমরা আসলে 32 টি বিট বা 8 টি বিট ব্যবহার করতেছি। কিন্তু উপরে আমরা দেখেছি 1 টি বিট দিয়েই আসলে একটি নাম্বার প্রাইম কিনা তা বলে দিতে পারি। সুতরাং একটি নাম্বারের জন্য একটি int নিয়ে 32 টি বিটের মাঝে বাকি 31 টি বিটের কোন ব্যবহার করতেছি না বা, bool টাইপ নিলে 8 টি বিটের মাঝে বাকি 7 টি বিটের কোন ব্যবহার করতেছি না।
সুতরাং আমরা যদি একটি int এর 32 টি বিটকেই কাজে লাগাতে পারি তাহলে, একটি int দিয়ে 32 টি নাম্বার চেক করতে পারি প্রাইম কিনা। আবার দুইটি int দিয়ে 64 টি নাম্বার দেখতে পারি প্রাইম কিনা। একই ভাবে আমরা n টি নাম্বার চেক করার জন্য n/32 টি int নিয়েই কাজ চালিয়ে নিতে পারি। মানে মেমোরি কম লাগবে 32 ভাগের 31 ভাগ।
এখন তাহলে কোড দেখার পালা। তার আগে আমি আশা করছি নিজেই একবার চেষ্টা করে দেখবেন কোড দেখার আগে। বিটওয়াইজ অপারেটর থেকে দেখেছি একটি বিট কীভাবে 0 থাকলে 1 করতে হয়। আরেকটি কাজ হচ্ছে যদি id তম পজিশনে থাকি তাহলে mark অ্যারের id/32 তম পজিশনের ভ্যালুর 32 টি বিট কে ব্যবহার করব আর ভ্যালুটির কত তম পজিশনে আছি তা জানব id%32 দিয়ে। id = 35 যদি হয় তাহলে id/32=1 মানে দ্বিতীয় int এ আছি এবং id%32=3 মানে চতুর্থ পজিশনের বিট কে দেখব।
const int N = 1e3; int mark[N/32 + 1];
bool check(int x, int p) { /// here x as mark[i/32] and check p-th position is 1 or 0 in x return x & (1<<p); } int ON(int x, int p) { /// here x as mark[i/32] and make 0 into 1 in p-th position in x return x | (1<<p); }
void bit_sieve() { /// marked 0 and 1 as not prime mark[0] = 3; /// marked multiple of 2 as not prime for (int i = 4;i <= N;i += 2) { mark[i/32] = ON(mark[i/32], i%32); }
int lim = sqrt(N); for (int i = 3;i <= lim;i += 2) { if (check(mark[i/32], i%32) == false) { // marked multiple of i as not prime for (int j = i*i;j <= N;j += i+i){ mark[j/32] = ON(mark[j/32], j%32); } } } }
int main() { bit_sieve();
for (int i = 0;i <= N;i++) { if (check(mark[i/32], i%32)==false) { cout << i << '\n'; } } return 0; } |
যেহেতু, id/32 আর id>>5 একই এবং id%32 আর id&31 একই তাই আমরা / এবং % ব্যবহার না করে >> এবং & এই বিটওয়াইজ অপারেটরগুলোই ব্যবহার করতে পারি। কারণ, আমরা জেনেছি বিটওয়াইজ অপারেটর গুলো অ্যারিথমেটিক অপারেটর গুলোর থেকে দ্রুত কাজ করতে পারে।
const int N = 1e3; int mark[(N>>5) + 1];
bool check(int x, int p) { return x & (1<<p); } int ON(int x, int p) { return x | (1<<p); }
void bit_sieve() { mark[0] = 3; for (int i = 4;i <= N;i += 2) { mark[(i>>5)] = ON(mark[(i>>5)], i&31); }
int lim = sqrt(N); for (int i = 3;i <= lim;i += 2) { if (check(mark[(i>>5)], i&31) == false) { for (int j = i*i;j <= N;j += i+i){ mark[(j>>5)] = ON(mark[(j>>5)], i&31); } } } }
int main() { bit_sieve();
for (int i = 0;i <= N;i++) { if (check(mark[(i>>5)], i&31)==false) { cout << i << '\n'; } } return 0; } |
এই বিষয়ে আরো টিউটোরিয়াল।
টিউটোরিয়াল ১
টিউটোরিয়াল ২
No comments