ধরুন, একটা কাগজে "Hello" লিখে আপনার সামনে ধরলাম। আপনি বুঝবেন এটা পাঁচটা অক্ষর, একটা শব্দ। যদি একটা বিড়ালের ছবি দেখাই, বুঝবেন এটা একটা প্রাণীর ছবি। MP3 চালালে শুনবেন একটা গান।
কিন্তু কম্পিউটার এগুলোর কোনোটাই দেখে না। কম্পিউটারের কাছে "Hello" শব্দটা, বিড়ালের ছবি, আর আপনার প্রিয় গান — সব আসলে একই জিনিস। শুধু ০ আর ১।
আগের আর্টিকেলে জেনেছি একটা bit physically কেমন করে বেঁচে থাকে — voltage-এর একটা state, flip-flop-এ আটকানো।
কিন্তু, বাস্তব জগতের একটা অক্ষর, একটা রঙ, একটা সুর — এগুলো memory-তে ঢোকার আগে কীভাবে ০ আর ১-এ রূপ নেয়? সেই translation-এর গল্পটাই আজকের বিষয়।
প্রথম নিয়ম: সবকিছু আগে সংখ্যা হবে
একটা কথা মাথায় রাখা দরকার। কম্পিউটারের একটাই কাজ জানা আছে — voltage ধরে রাখা। High বা low। ১ বা ০।
আর voltage-এর combination দিয়ে সে যা প্রকাশ করতে পারে, সেটা শুধুই সংখ্যা। এর বাইরে কিছু না।
এই নিয়ম মেনেই বাকিসব বানানো হয়েছে। যদি text চান — প্রথমে সেই text-কে সংখ্যায় রূপান্তর করতে হবে। যদি ছবি চান — সেটাও প্রথমে সংখ্যায় ভাঙতে হবে। Sound-এর ক্ষেত্রেও একই কথা। তারপর সেই সংখ্যা binary-তে রূপ নেয়, আর সেই binary voltage হিসেবে memory-তে ঢুকে যায়।
তাহলে প্রথম প্রশ্ন — সংখ্যা নিজেই কীভাবে binary হয়?
সংখ্যা থেকে binary
মানুষ base-10 বা ডেসিমাল সিস্টেম ব্যবহার করে। কেন? সম্ভবত কারণটা সহজ — আমাদের ১০টা আঙুল আছে। ০ থেকে ৯ পর্যন্ত ১০টা digit, আর প্রতিটা column-এর মান ১০-এর power হিসেবে বাড়ে — একক, দশক, শতক, সহস্র।
কম্পিউটারের সুইচ বা transistor বোঝে মাত্র দুইটা state — on বা off। তাই তার জন্য বানানো হয়েছে base-2 বা binary সিস্টেম। এখানে digit শুধু দুইটা (০ আর ১), আর প্রতিটা column-এর মান ২-এর power হিসেবে বাড়ে — ১, ২, ৪, ৮, ১৬, ৩২ — এভাবে।
কেন ঠিক ২-এর power? কারণটা মজার। প্রতিটা নতুন bit আসলে আগের সব bit-এর ক্ষমতাকে দ্বিগুণ করে দেয়। এক bit-এ দুইটা possibility থাকে (০ অথবা ১)। দুই bit যোগ করলে চারটা possibility (০০, ০১, ১০, ১১)। তিন bit-এ আটটা, চার bit-এ ষোলটা। এভাবেই সম্ভাবনার সংখ্যা exponentially বাড়তে থাকে।
তাহলে এই বাইনারি কলামগুলো দিয়ে আমরা সংখ্যা বানাবো কীভাবে? নিয়মটা সহজ: যে যে কলামের মান আপনার যোগ করতে হবে, সেগুলোকে 1 (ON) করে দিন, আর বাকিগুলোকে 0 (OFF)। নিচের যন্ত্রে নিজেই ১৩ বানিয়ে দেখুন:
উদাহরণ হিসেবে, ডেসিমাল ১৩ সংখ্যাটার কথা ধরা যাক। আমাদের কলামগুলো হলো ৮, ৪, ২, ১। ১৩ বানাতে আমাদের লাগবে একটা ৮, একটা ৪, এবং একটা ১ (যেহেতু ৮ + ৪ + ১ = ১৩)। ২ আমাদের লাগছে না। তাই বাইনারিতে এর রূপ হবে 1101।
গণিতের ভাষায় হিসাবটা দাঁড়ায় এরকম:
1101₂ = (1×8) + (1×4) + (0×2) + (1×1) = 13₁₀
পজিটিভ সংখ্যা তো বুঝলাম, কিন্তু কম্পিউটার মাইনাস (−) চিহ্ন মেমোরিতে রাখবে কীভাবে? মেমোরিতে তো প্লাস-মাইনাস বলতে কিছু নেই, আছে শুধু ভোল্টেজ।
প্রথম দেখায় মনে হতে পারে, আমরা হয়তো বাইনারি সংখ্যার একদম বামের বিটটাকে (Most Significant Bit) সাইন নির্ধারণের জন্য রেখে দিতে পারি — 0 মানে প্লাস, 1 মানে মাইনাস। এই পদ্ধতিকে বলা হয় Sign-Magnitude। কিন্তু এই সরল চিন্তার মধ্যে একটা বড় খুঁত আছে। এভাবে হিসেব করলে কম্পিউটারে +0 এবং −0 নামে দুটি আলাদা বাইনারি রূপ তৈরি হয় (যেমন ৮-বিটে 00000000 এবং 10000000)। গণিতে শূন্যের কোনো সাইন হয় না, আর হার্ডওয়্যার লেভেলে দুটি আলাদা শূন্যের অস্তিত্ব থাকা মানেই যোগ-বিয়োগের লজিক সার্কিট জটিল হয়ে যাওয়া।
এই সমস্যার সবচেয়ে চমৎকার এবং ইঞ্জিনিয়ারিং-বান্ধব সমাধান হলো Two's Complement।
এখানে নেগেটিভ সংখ্যা বের করার ট্রিকটা খুব সহজ: প্রথমে সংখ্যাটির পজিটিভ রূপের প্রতিটা বিট উল্টে দিন (0-কে 1 আর 1-কে 0 করুন, যাকে বলে One's Complement), তারপর সেই ফলের সাথে জাস্ট 1 যোগ করুন। এই এক নিয়মেই পজিটিভ এবং নেগেটিভ সংখ্যার সাধারণ যোগ-বিয়োগ একই ALU (Arithmetic Logic Unit) সার্কিট দিয়ে বাড়তি কোনো প্লাস-মাইনাস লজিক ছাড়াই পানির মতো নিখুঁতভাবে করা সম্ভব হয়।
সংখ্যা তো হলো। কিন্তু বাস্তব জীবনে আপনি তো শুধু সংখ্যা লেখেন না। যখন চ্যাটবক্সে "Hello" লেখেন, সেই অক্ষরগুলো কীভাবে binary হয়?
তথ্য যদি হয় text
Text-কে binary বানানোর ট্রিকটা খুব সহজ — প্রতিটা অক্ষরের জন্য একটা করে নির্দিষ্ট সংখ্যা বরাদ্দ করা।
Computing-এর একেবারে শুরুর দিকে এই standard-এর নাম ছিল ASCII। প্রতিটা ইংরেজি অক্ষর, digit, punctuation — সবার জন্য একটা করে ৭-bit code:
বড় হাতের A → 65 → 01000001 ছোট হাতের a → 97 → 01100001
ইংরেজির জন্য ASCII ঠিকঠাকই কাজ করত। কিন্তু সমস্যা শুরু হলো যখন অন্য ভাষা বা emoji-র প্রয়োজন পড়ল। ৭ bit দিয়ে সর্বোচ্চ ১২৮টা character-এর জায়গা হয়। শুধু বাংলা বর্ণমালার সব অক্ষর ধরার মতোই সেখানে জায়গা নেই, পৃথিবীর হাজার হাজার ভাষার কথা তো বাদই দিলাম।
শুরুর দিকে যখন Python বা C দিয়ে database-এ বাংলা input নেওয়া হতো, প্রায়ই screen-এ কিছু অদ্ভুত হিজিবিজি character দেখাত। প্রোগ্রামাররা ভাবতো, আমি লিখলাম বাংলা, screen-এ কেন এই garbage? এগুলো আবার কি?
Computer আসলে বাংলা character-কে ভুল dictionary দিয়ে decode করার চেষ্টা করছিল। ASCII-র dictionary-তে বাংলা নেই। ছিলই না।
এই সমস্যার সমাধান হলো Unicode — একটা international standard, যেটা পৃথিবীর প্রতিটা ভাষার প্রতিটা character-এর জন্য একটা করে unique code point নির্ধারণ করে দেয়।
এখানে একটা সূক্ষ্ম পার্থক্য আছে যেটা বেশিরভাগ মানুষ ধরতে পারে না, কারণ Unicode আর UTF-8 কথাগুলো প্রায়ই interchangeably ব্যবহার হয়। কিন্তু এরা এক জিনিস না।
Unicode বলে দেয় প্রতিটা character-এর পরিচয়পত্র কী হবে। আর সেই পরিচয়পত্রটা memory-তে actual bit হিসেবে কীভাবে লেখা হবে — সেটা বলে UTF-8।
Unicode একটা mapping — পরিচয় বণ্টনের নিয়ম। UTF-8 হলো encoding — সেই পরিচয়কে actual bit-এ রূপান্তরের নিয়ম।
UTF-8 হলো একটা variable-width encoding। ইংরেজি অক্ষরের জন্য এটা ৮ bit (১ byte) ব্যবহার করে — যেটা ASCII-র সাথে পুরোপুরি backward-compatible। বাংলা character-এর জন্য ৩ byte, আর emoji-র মতো জটিল character-এর জন্য ৪ byte পর্যন্ত লাগতে পারে।
যেহেতু একটা অক্ষরের জন্য তখন একের অধিক byte লাগছে, মেমোরিতে এই byte-গুলো কোন সিরিয়ালে বসবে (Endianness) সেটাও কম্পিউটারকে হিসাব করতে হয়।
Text-এর গল্প এতটুকুই। তথ্যের সেই টুকরো যদি একটা অক্ষর হতো, এতক্ষণে সে binary হয়ে memory-তে ঢোকার জন্য প্রস্তুত।
কিন্তু তথ্য যদি ছবির একটা অংশ হয়?
তথ্যের টুকরো যদি হয় image
যেকোনো digital ছবিকে খুব কাছ থেকে দেখলে দেখা যাবে, এটা আসলে কোটি কোটি ক্ষুদ্রাতিক্ষুদ্র বিন্দুর সমষ্টি। প্রতিটা বিন্দুকে বলে pixel।
Screen-এ রঙ তৈরি করার জন্য ব্যবহার হয় RGB model। প্রতিটা pixel মূলত তিনটা primary color-এর মিশ্রণ — Red, Green, Blue।
প্রতিটা color কতটা উজ্জ্বল হবে, সেটার জন্য ০ থেকে ২৫৫ পর্যন্ত একটা brightness value দেওয়া হয়। ২৫৫ মানে সর্বোচ্চ উজ্জ্বলতা, ০ মানে সম্পূর্ণ off। ২৫৫ পর্যন্ত সংখ্যা store করতে প্রতি color channel-এর জন্য ৮ bit (১ byte) লাগে।
তাহলে একটা pixel-এর রঙ প্রকাশ করতে মোট প্রয়োজন: ৮ + ৮ + ৮ = ২৪ bit। এটাকেই বলে 24-bit color depth। নিচের যন্ত্রে যেকোনো pixel-এ চাপ দিয়ে তার binary রূপ দেখুন:
Computer এই pixel value-গুলোকে row-by-row সাজিয়ে পুরো ছবিটাকে memory-তে একটা bit sequence হিসেবে লিখে রাখে।
এই জায়গায় একটা হিসাব করে দেখা যাক। আপনার screen যদি 1920×1080 resolution-এর হয়, তাহলে এক মুহূর্তে প্রায় ২০ লক্ষ pixel-এর রঙ আলাদাভাবে মনে রাখতে হয়। প্রতিটার জন্য ২৪ bit। মানে শুধু একটা frame render করতেই লাগবে ৬ MB। কিন্তু বাস্তবে একটা ছবি বা ভিডিওর একটা ফ্রেম তো এত জায়গা নেয় না। কিভাবে? সেই গল্প একটু পরে।
Image-এর গল্পও শেষ। কিন্তু কান দিয়ে যা শুনি? বাতাসে ভেসে আসা একটা সুর — সেটা তো কোনো অক্ষর না, কোনো pixel-ও না। সেটাকে কীভাবে bit বানাব?
তথ্যের টুকরো যদি হয় sound
শব্দ মূলত একটা continuous pressure wave — বাতাসের একটা কাঁপুনি। Computer continuous কিছু বোঝে না। বোঝে কেবল discrete সংখ্যা।
তাহলে কীভাবে continuous জিনিসকে discrete করা যায়?
একটা analogy দিয়ে ভাবা যাক। ধরুন আপনি একটা দৌড়ের video করছেন। যদি প্রতি এক সেকেন্ডে একটাই ছবি তোলেন, video-টা খুব খাপছাড়া লাগবে — এক মুহূর্তে দৌড়বিদ এক জায়গায়, পরের মুহূর্তে হঠাৎ অনেক দূরে। কিন্তু প্রতি সেকেন্ডে যদি ৬০টা করে ছবি তোলেন, হঠাৎ করে সেটা smooth চলমান video-তে পরিণত হবে।
শব্দের ক্ষেত্রেও ঠিক তাই। পুরো wave-টা ধরে রাখা হয় না। বরং খুব দ্রুত, খুব অনেকগুলো "ছবি" তুলে রাখা হয়। প্রতিটা "ছবি" মানে — ঠিক এই মুহূর্তে wave-টার উচ্চতা (amplitude) কতটুকু।
এই প্রক্রিয়ার নাম sampling। নিচের যন্ত্রে rate কমিয়ে-বাড়িয়ে দেখুন wave-টা কতটা বিশ্বস্তভাবে ধরা পড়ে:
একটা audio কতটা নিখুঁত হবে, সেটা নির্ভর করে দুটি জিনিসের ওপর:
Sample Rate: প্রতি সেকেন্ডে কতবার wave-এর উচ্চতা মাপা হচ্ছে। CD quality audio-র standard হলো 44,100 Hz — মানে প্রতি সেকেন্ডে ৪৪,১০০ বার measurement।
Bit Depth: প্রতিটা measurement store করতে কত bit ব্যবহার হবে? সাধারণত ১৬ বা ২৪ bit। যত বেশি bit, তত বেশি precise — শব্দের সূক্ষ্ম detail তত ভালোভাবে ধরা পড়ে।
তারমানে, তথ্যের টুকরো এখন আর অক্ষর, pixel বা sound না। সব রূপে সে এখন সংখ্যা। আর সংখ্যা মানেই bit। একটা বিশাল রহস্য উন্মোচিত হলো।
কিন্তু এখানে একটা সমস্যা আছে।
সমস্যা — সব কিছুই বিশাল
যদি text, image, sound-এর প্রতিটা কণা এভাবে memory-তে লিখে রাখা হয়, তাহলে file size হবে ভয়াবহ:
3-minute গান → ~30 MB (raw) HD ছবি → ~6-10 MB (raw) 1-hour 4K video → ~100+ GB (raw)
এত বড় file-এর ভার internet সহ্য করতে পারবে না। Instagram-এ ছবি upload করতে ঘণ্টা লেগে যাবে। YouTube video load-ই হবে না।
সমাধান হলো compression। ছোট করে ফেলা।
আর এই magic-টা আসলে দুই ধরনের।
Compression: চতুরভাবে ছোট করা
lossless compression হচ্ছে সেই ধরনের compression যেখানে কিছুই হারায় না। File compress করলাম, পরে decompress করলাম — original ফিরে পেলাম, একটাও bit বদলালো না।
এটা কীভাবে সম্ভব? আসল ট্রিকটা হলো — data-র মধ্যে থাকা repetition বা pattern খুঁজে বের করা।
একটা সহজ example। কেউ যদি জিজ্ঞেস করে, নিচের জিনিসটা লেখার সহজ উপায় কী?
AAAAAAAAAAAAAAAAAA
আপনি হয়তো বলবেন — "18 × A"।
দুটোই একই কথা প্রকাশ করে, কিন্তু দ্বিতীয়টা অনেক ছোট। এটাই Run-Length Encoding (RLE)-এর মূল আইডিয়া। পর পর একই জিনিস থাকলে সেটা বারবার না লিখে জাস্ট "কতবার" আর "কী" লিখে দেওয়া। যদি কোনো ছবিতে পর পর ৫০টা সাদা pixel থাকে, RLE লিখবে 50 × White — ৫০টা আলাদা pixel value store করার বদলে একটা জোড়া।
আরেকটা technique হলো Huffman coding। এটার idea-টা দারুণ। ধরুন আপনি আর আপনার বন্ধু প্রতিদিন 100 বার একটা phrase লেখেন — "ঠিক আছে"। এতবার লিখতে গিয়ে অনেক সময় খরচ হয়। যদি দুজন মিলে আগে থেকে ঠিক করে রাখেন, একটা ⭐ চিহ্ন মানে হবে "ঠিক আছে" — তাহলে একই তথ্য অনেক কম জায়গায় প্রকাশ করা যাবে।
Huffman-এর মূল আইডিয়া ঠিক এমনই। File-এ সবচেয়ে বেশিবার আসা data-কে সবচেয়ে ছোট code দাও। কম আসা data-কে বড় code। মোট size তখন দ্রুত কমে যায়।
Huffman algorithm-এর গভীরে গেলে tree structure, priority queue — অনেক জিনিস আসবে। কিন্তু এখনের জন্য এই basic intuition-টাই যথেষ্ট।
Developer হিসেবে যখন browser-এ HTML, CSS, JS পাঠান, সেখানে lossless compression (Gzip বা Brotli) mandatory। কারণ ওখানে একটা semicolon হারিয়ে গেলেই code ভেঙে পড়বে। Lossless-এ কিছুই হারায় না, তাই এটা নিরাপদ।
lossy compression অন্য জিনিস। এখানে ইচ্ছাকৃতভাবে কিছু data চিরতরে মুছে ফেলা হয় — কিন্তু এমনভাবে, যাতে মানুষের চোখ বা কান পার্থক্য ধরতে না পারে।
Sound-এ: MP3 format মানুষের শোনার সীমাবদ্ধতা কাজে লাগায়। যে frequency-গুলো মানুষ শুনতেই পায় না, সেগুলো বাদ দিয়ে দেওয়া হয়। একই মুহূর্তে যদি একটা জোরালো শব্দ চলে, তার পাশে একটা হালকা শব্দ থাকলে — মানুষের কান হালকা শব্দটা ধরতে পারে না, তাই সেটাও বাদ।
Image-এ: JPEG format মানুষের চোখের বৈশিষ্ট্যকে কাজে লাগায়। আমাদের চোখ রঙের চেয়ে আলোর তারতম্য বেশি ভালোভাবে ধরে। তাই ছবির রঙের তথ্য একটু কমিয়ে দিলে চোখ ধরতে পারে না, কিন্তু file অনেক ছোট হয়ে যায়।
Video-তে: এক frame থেকে পরের frame-এ যে অংশগুলো পরিবর্তন হচ্ছে না (যেমন background), সেগুলো নতুন করে store না করে আগের frame থেকে reference নেওয়া হয়। একটা এক-ঘণ্টার video-তে বেশিরভাগ frame-ই আসলে আগের frame-এর সামান্য variation। H.264, H.265, AV1 — এই সব encoding format এই আইডিয়াটাকেই কাজে লাগায়।
সহজ rule of thumb — media (image, audio, video)-এর জন্য lossy compression যথেষ্ট। কিন্তু code, text, database backup-এর জন্য lossless ছাড়া উপায় নেই।
CPU কিন্তু কিছুই বোঝে না
এই জায়গায় একটা weird ব্যাপার আছে।
তথ্যের সেই টুকরোটা যখন memory-তে voltage হিসেবে বসে আছে, CPU-র কাছে সেটা কী? একটা অক্ষর? একটা pixel? একটা sound sample?
আসলে কিছুই না।
CPU-র কাছে 01000001 মানে কেবল আটটা voltage-এর একটা pattern। এটা "A" না। এটা "65" সংখ্যাও না। এটা কোনো "লাল shade"-ও না। এটা শুধু voltage।
CPU কখনো ভাবে না — "আরে! এটা তো একটা বিড়ালের ছবি!" কিংবা "এটা তো বাংলা language!"
সে শুধু instruction follow করে যায়। ঠিকঠাক voltage সরায়।
তাহলে অর্থটা তৈরি হয় কোথায়? সফটওয়্যারে। যে application এই bit sequence খুলছে, সে-ই ঠিক করে দেয় এটা কী রূপে দেখানো হবে:
Same bit, ভিন্ন meaning। Software gives meaning। CPU is blind।
পুরো গল্পটা একবার
এবার সেই তথ্যের টুকরোর পুরো যাত্রা একবার চোখের সামনে চালিয়ে দেখা যাক।
আপনি লিখলেন:
const message = "Hello";
- প্রথমে প্রতিটা অক্ষর একটা করে Unicode code point পেল
- UTF-8 সেই code point-গুলোকে bit-এ রূপান্তর করে দিল
- যদি network-এ পাঠানো হয়, Gzip এসে সেই bit-গুলোকে আরও ছোট করে দিল
- Memory controller সেই bit-গুলোকে RAM-এর flip-flop-এ voltage হিসেবে বসিয়ে দিল
- পরে CPU যখন এই ডেটা পড়তে চাইল, সেই voltage read করে ফেরত আনল
- আপনার JavaScript engine সেই bit-গুলোকে interpret করে আবার সেই আগের "Hello" শব্দটাই ফিরিয়ে দিল
পুরো process-এ same information বার বার রূপ বদলেছে। বাতাসের কম্পন থেকে সংখ্যা। সংখ্যা থেকে bit। বিট থেকে voltage। আবার voltage থেকে সংখ্যা, সংখ্যা থেকে অক্ষর।
Meaning একই থেকেছে। শুধু রূপ পাল্টেছে।
সমস্ত computing-এর গল্পটা মূলত এটাই — একই তথ্যের একের পর এক translation, কিন্তু meaning একই থাকা।
- কম্পিউটার কোনো ছবি "দেখে" না, কোনো গান "শোনে" না — সব তথ্যই শেষ পর্যন্ত bit-এর একটা sequence।
- বাস্তব জগতকে digital করার আলাদা আলাদা dictionary আছে — text-এর জন্য Unicode + UTF-8, image-এর জন্য RGB pixel grid, sound-এর জন্য sampling।
- Compression দুই ধরনের — lossless (কিছুই হারায় না, code/text-এর জন্য mandatory) আর lossy (মানুষের ইন্দ্রিয়ের সীমাবদ্ধতা কাজে লাগায়, media-র জন্য standard)।
- Meaning-টা software-এ, bit-এ না — একই bit sequence context ভেদে অক্ষর, সংখ্যা বা pixel হতে পারে।