Header Ads

বিটওয়াইজ অপারেটর(Bitwise Operator)



যোগ, বিয়োগ, গুণ ও ভাগ ছাড়াও কম্পিউটার দিয়ে আরো অনেক ধরনের হিসাব করা যায়, তারমাঝে বিটওয়াইজ অপারেটরের ব্যবহার অন্যতম। এছাড়া, কম্পেটিটিভ প্রোগ্রামিংয়ে বিটওয়াইজ অপারেটরের রয়েছে বিস্তর ব্যবহার। তাই, এই লেখায় বিটওয়াইজ অপারেটরগুলো নিয়ে একটু আলোচনা করার চেষ্টা করব। শুরু করার আগে এর একটি উপকারী দিক না বললেই নয়। বিটওয়াইজ অপারেটরগুলো সরাসরি বিট 0 বা, 1 নিয়ে কাজ করে। তাই, বিটওয়াইজ অপারেটরগুলো অ্যারিথম্যাটিক অপারেটরের থেকে অনেক দ্রুত কাজ করতে পারে। আর কথা না বাড়িয়ে সরাসরি অপারেটরগুলোর ব্যবহারে চলে যাই।

প্রথমেই দেখা যাক AND অপারেটর নিয়ে। কম্পিউটারে এটিকে & দ্বারা প্রকাশ করা হয়। এবার একটা উদাহরণ দেখি,
অফ & অফ = অফ
অফ & অন = অফ
অন & অফ = অফ
অন & অন = অন 
মানে দুইপাশেই অন থাকলে তাহলেই শুধু উত্তর অন থাকবে তাছাড়া অফ থাকবে। আর কম্পিউটার যেহেতু অফ মানে বুঝে 0 এবং অন মানে বুঝে 1 তাই উদাহরণ টি এখন সংখ্যা দিয়ে লিখতে পারি।
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1
এখন তাহলে 6 & 3 সমান কত হবে? যেহেতু AND অপারেটর শুধু 0 আর 1 নিয়ে কাজ করে তাই সংখ্যা দুইটি বাইনারিতে উপস্থাপন করলে বুঝতে সুবিধা হবে। 6 কে বাইনারিতে উপস্থাপন করলে হয় 0110 এবং 3 কে বাইনারিতে উপস্থাপন করলে হয় 0011। তাহলে, 
   0110
&0011
----------
   0010
এখন যদি 0010 কে ডেসিমালে উপস্থাপন করি তাহলে আমরা জানি সেটি হবে 2। এই যে "ডেসিমাল থেকে বাইনারি এবং বাইনারি থেকে ডেসিমাল" এই কাজগুলো কিন্তু আমাদের করতে হবে না, কম্পিউটার নিজেই করে নেয়। আমাদের শুধু দুইটি সংখ্যার মাঝে অপারেটর গুলো বসাতে হবে। আর শুধু অপারেটগুলো আসলে কীভাবে কাজ করে তা ভালো করে বুঝার জন্য বাইনারিতে উপস্থাপন করে দেখতেছি। AND অপারেটরের ব্যবহার নিয়ে একটি কোড দেখে নেই,


#include <bits/stdc++.h>


using namespace std;


int main()

{

  int n, m;


  cin >> n >> m;


  int ans = n & m;


  cout << ans << '\n';


  return 0;

}


কিছু সংখ্যাকে যদি বাইনারিতে উপস্থাপন করে দেখি তাহলে দেখা যাবে, জোড় সংখ্যার ডানের বিট সবসময় 0 এবং বিজোড় সংখ্যার ডানের বিট সবসময় 1। তাহলে AND অপারেটর দিয়েও কোন সংখ্যা জোড়/বিজোড় বের করে ফেলতে পারি। 
if (n&1 == 0)even;
else odd;
% অপারেটরের থেকে & অপারেটর দ্রুত কাজ করে।

OR অপারেটর কিন্তু আবার AND অপারেটরের মত অত কঠিন মনের অধিকারী না। এর দুইপাশের যেকোন একটি অন মানে 1 থাকলেই উত্তর হবে 1। উদাহরণ টি দেখে নেই তাহলে, আর কম্পিউটারে এটিকে | দ্বারা প্রকাশ করা হয়। 
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
এবার তাহলে 6 | 3 সমান কত? বাইনারিতে উপস্থাপন করে দেখে নেই তাহলে,
   0110
 |0011
----------
   0111
0111 কে ডেসিমালে উপস্থাপন করলে হয় 7। সুতরাং 6 | 3 = 7। তাহলে কোডে লিখতে পারি, ans = n | m;

XOR অপারেটরের বৈশিষ্ট্য একটু মজার, এটি মিলেমিশে থাকাকে পছন্দ করতে পারে না :| মানে দুইপাশে 0 বা 1 থাকলে এটি 0 উত্তর দেয় এবং দুইপাশে বিপরীত সংখ্যা থাকলে তাহলেই কেবল 1 উত্তর দেয়। কম্পিউটার XOR অপারেটর বলতে বুঝে ^ এটিকে। উদাহরণ,
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
আবার একই প্রশ্ন। এবার তাহলে 6 ^ 3 সমান কত? বাইনারিতে উপস্থাপন করে দেখতে পারি।
   0110
 ^0011
----------
   0101
0101 কে ডেসিমানেল উপস্থাপন করলে হয় 5। সুতরাং 6 ^ 3 = 5। তাহলে কোডে লিখতে পারি, ans = n ^ m;

এবার আলোচনা করব NOT অপারেটর নিয়ে। কম্পিউটারে এটিকে ~ দ্বারা প্রকাশ করা হয়। NOT অপারেটরের বৈশিষ্ট্য আগের তিন অপারেটরের থেকে একটু ভিন্ন। এটি একটি সংখ্যার সব বিট কেই বিপরীত করে দেয়। মানে 0 থাকলে 1 করে দেয় এবং 1 থাকলে 0 করে দেয়। 
int n = 5;
n = ~n;
cout << n << '\n';
output: -6
কিন্তু কীভাবে!!! 
এখানে আমি int নিয়ে কাজ করতেছি আর int কাজ করে 32 বিট নিয়ে আর এর মাঝে 31 টি বিট সংখ্যা নির্ধারণ করে আর 32 তম বিট টি সাইন হিসেবে কাজ করে। মানে 32 তম বিট টি 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 0 0 0 0 1 0 1(বাইনারি) = 5 (ডেসিমাল)
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0(বাইনারি) = -6 (ডেসিমাল)
তাহলে সহজেই বুঝা যাচ্ছে 32 তম বিট টি সাইন বিট মানে এটিই নির্ধারণ করে একটি সংখ্যা পজিটিভ হবে নাকি নেগেটিভ। আর NOT অপারেটর সাইন বিট টি কেও পরিবর্তন করে দিচ্ছে তাই পজিটিভ সংখ্যা হয়ে যাচ্ছে নেগেটিভ। এখন তাহলে প্রশ্ন হতে পারে, 
0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 = 2147483642 হলে, 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 = -2147483642 না হয়ে -6 হল কীভাবে? পাল্টা প্রশ্ন করি, -2147483642 বড় নাকি -6 বড়?
আমরা জানি, int এ সবথেকে বড় সংখ্যা রাখা যায় এটি 2147483647 এবং সবথেকে ছোট সংখ্যা রাখা যায় এটি -2147483648। পজিটিভ সংখ্যার শেষে 7 এবং নেগেটিভ সংখ্যার শেষে 8 কেন? এটি নিশ্চয়ই বুঝে যাওয়ার কথা। না বুঝলেও সমস্যা নেই, একটু সময় নিয়ে ভেবে দেখলেই এই বিষয়গুলো পরিষ্কার হয়ে যাবে।

এবার আলোচনা করব Left Shift অপারেটর নিয়ে। নাম দেখেই হয়ত অনেকটা বুঝা যাচ্ছে এর কাজ কেমন। এই অপারেটর একটি সংখ্যার সবগুলো বিট কে বামে সরানোর কাজ করে। (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 0 1) = (5)  এই সংখ্যার বিটগুলোকে যদি আমি 2 ঘর বামে সরাই তাহলে সংখ্যাটি হবে (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 0 1 0 0) = (20)। ঠিক আছে, বুঝলাম, দুই ঘর বামে সরানো মানে নতুন দুইটি 0 কে ডানে বসিয়ে বাকিগুলোকে দুই ঘর করে বামে সরাচ্ছি কিন্তু যে দুইটি 0 একেবারে বামে ছিলো তাদের কি হবে? n নামক গ্রামটিতে জায়গা স্বল্পতার জন্য তাদের n নামক গ্রামটি থেকে বের করে দেওয়া হয়েছে :| 
int n = 5;
n = n << 2; // now n became 20
cout << n << '\n';
output: 20
এখানে মজার বিষয় হচ্ছে, << অপারেটর দিয়ে 2 এর পাওয়ার খুব সহজেই বের করা যায়। যেমন,  
cout << (1<<3) << '\n'; // 2⁴ = 14;
cout << (1<<4) << '\n'; // 2⁵ = 32;
সতর্কতাঃ Left Shift অপারেটর ও আউটপুট দেখানোর জন্য cout এ ব্যবহার করা অপারেটরের কাজ অনেকটা একই কিন্তু Left Shift এর কাজ বিট নিয়ে অপরটির কাজ সবরকম ডাটা নিয়ে দেওয়া cout এর কাছে।

বিটওয়াইজ অপারেটরের শেষ অপারেটর Right Shift নিয়ে আলোচনা করব এখন। এটি প্রায় Left Shift অপারেটরের মতই কাজ করে তবে শুধু বাম দিকে না সরিয়ে ডান দিকে সরায়। যেমন, (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 0 1) = (5) সংখ্যাটির বিটগুলোকে যদি 2 ঘর ডানদিকে সরাই তাহলে সংখ্যাটি হবে (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)। 
int n = 5;
n = n >> 2; // now n became 1
cout << n << '\n';
output: 1

এখন মজার দুইটি বিষয় দেখব আমরা। বিষয় দুইটি হচ্ছে একটি সংখ্যার কোন একটি নির্দিষ্ট ডিজিট কে বদলে দেওয়া। মানে 1 কে 0 করে দিব বা 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 0 0 0 0 0 0 0)বাইনারি = (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 0 0 0 0 0 0 1)বাইনারি = (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 1 0 0 1)বাইনারি = (9)ডেসিমাল
এখন যদি প্রথম বিট টি 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 0 0 0)বাইনারি = (8)ডেসিমাল
অফ থাকা বিট কীভাবে অন করতে হয় এবং অন থাকা বিট কীভাবে অফ করতে হয় তার একটি প্রোগ্রাম দিয়ে দিচ্ছি। 


#include <bits/stdc++.h>


using namespace std;


int off(int n, int p) {

  return (n & (~(1<<p)));

}


int on(int n, int p) {

  return (n | (1<<p));

}


int main()

{

  int n = 0;


  n = on(n, 0); // on the first bit

  cout << n << '\n';


  n = on(n, 3); // on the fourth bit

  cout << n << '\n';


  n = off(n, 0); // off the first bit

  cout << n << '\n';


  return 0;

}


বিটওয়াইজ অপারেটরগুলো নিয়ে কাজ করার সময় টাইপ কাস্টিং এর বিষয়ে সতর্ক থাকতে হবে।

এই বিষয়ে আরো টিউটোরিয়াল।
টিউটোরিয়াল ১

No comments

Powered by Blogger.