Module ৪ · Operator আর type কনভার্শন
Problem: যে expression কামড় দেয়
এই lesson-এ যা শিখবে
- প্রথম লাইন লেখার আগেই একটা কাজের জন্য ঠিক operator-টা বেছে নিতে পারবে।
- Input ঋণাত্মক হতে পারলে ফলের চিহ্ন কী হবে সেটা আগেই বলতে পারবে।
- Statement আগেই যে দুই প্রান্তের কথা বলেছে, সেখানে একটা expression পরীক্ষা করতে পারবে।
দশটা problem, লুকানো test দিয়ে গ্রেড হয়। তার আটটা তুমি ছয়টা lesson-র ভিতরেই পেয়েছ। দুইটা নতুন।
গত module ছিল type বাছা নিয়ে। এটা operator বাছা নিয়ে, আর তারপর ওই operator প্রান্তে গিয়ে কী করে তা নিয়ে।
নিচের প্রায় প্রতিটা ব্যর্থতাই একটা চিহ্ন নয়তো একটা চওড়া। Sample-র জন্য মানটা ঠিক, আর statement যে input নিয়ে সাবধান করেছিল তার জন্য ভুল।
গত problem set-র পর থেকে নতুন কী
Module 2-র প্রতিটা problem-এ একটা করে ভাবনা ছিল। এখানে কয়েকটায় দুইটা, আর দ্বিতীয়টা শুধু প্রান্তেই দেখা দেয়।
চারটা problem ঋণাত্মক input নেয়। তার তিনটায় / বা % আছে, যেখানে lesson 1-র চিহ্নের নিয়মটাই উত্তর ঠিক করে।
তিনটা problem bit নিয়ে কাজ করে, যেখানে type-টাকে unsigned হতে হয় আর literal-টাকে একটা u বইতে হয়।
তাই গল্পের আগে Constraints পড়ো, আর যেখানে যেখানে মাইনাস চিহ্ন চলতে পারে সব দাগিয়ে রাখো।
এখনো if নেই, এখনো loop নেই
Module 5 আর 6 এখনো আসেনি। নিচের কোনো কিছুতেই if, for বা while লাগে না, আর কোনোটাতেই ওটা থাকার কথা না।
চারটা problem হ্যাঁ-না প্রশ্ন করে। উত্তরটা একটা তুলনা, যেটা 1 বা 0 মানের একটা expression, আর %d দিয়ে ছাপা হয়।
তিনটা problem-এ এমন একটা মান থেকে ভাগশেষ লাগে যেটা ঋণাত্মক হতে পারে, আর ওখানে lesson 1-র চিহ্নের নিয়মই উত্তর ঠিক করে।
তাই তুমি যদি এমন কিছুতে হাত বাড়াও যা এখনো শেখোনি, problem-টা তোমাকে সামনে তাকাতে না বলে একটা lesson আবার পড়তে বলছে।
চিহ্নের যে দুই নিয়ম এই দশটার চারটা ঠিক করে
দুইটাই lesson 1-র, আর শুরু করার আগে দুইটাই আবার বলার মতো।
Integer division শূন্যের দিকে কাটে। -9 / 2 হলো -4, -5 না। ভগ্নাংশটা ফেলে দেওয়া হয়, আর ফেলে দেওয়া মানে নিচের দিকে round করা না।
ভাগশেষ বাঁ পাশের মানের চিহ্ন নেয়। -7 % 3 হলো -1, 2 না। তাই "এটা কি ভাগ যায়" প্রশ্নে ভাগশেষকে 0-র সাথে মেলাতে হয়, কখনো কোনো ধনাত্মক সংখ্যার সাথে না।
যে program ভাগশেষকে 1-র সাথে বা কোনো ধনাত্মক সংখ্যার সাথে মেলায়, ওটা ঋণাত্মক নয় এমন প্রতিটা test পাশ করবে আর প্রথম ঋণাত্মকটাতেই পড়বে।
unsigned, আর u-টা কোথায় বসে
এখানে তিনটা problem bit নিয়ে কাজ করে। তিনটাই %u দিয়ে একটা unsigned int-এ পড়ে আর %u দিয়েই ছাপে।
ওদের ভিতরে প্রতিটা mask লেখা হয় 1u << k, কখনো 1 << k না। Bit নম্বর 31 হলে দ্বিতীয়টা undefined behaviour, আর Playground তোমাকে সতর্কও করবে না।
একটা problem, শেষেরটা, ঠিক উল্টো শিক্ষা: একটা unsigned মান একটা signed মানের সাথে তুলনায় এলে কী গোলমাল হয় সেটা নিয়ে।
এই দশটা problem-র যে রূপগুলো লাগে
scanf("%d", &n) একটা int, যেটা ঋণাত্মক হতে পারে
scanf("%u", &n) একটা unsigned int, bit-র problem-র জন্য
printf("%d\n", a < b) একটা তুলনা, 1 বা 0 হিসেবে ছাপা
printf("%u\n", v) একটা unsigned মান
n / 10 % 10 একটা সংখ্যার মাঝখান থেকে একটা অঙ্ক
n % d == 0 ভাগ যায় কি না, আর ঋণাত্মক n-এও ঠিক
v |= (1u << k) bit k বসাও
v &= ~(1u << k) bit k মুছে দাও
v ^= (1u << k) bit k উল্টে দাও
(v >> k) & 1u bit k পরীক্ষা করো, মান 1 বা 0
- নিচের প্রতিটা starter-এ তার
scanfলাইনটা আগেই লেখা। ওটা যেমন আছে তেমনই রাখো। - হ্যাঁ-না উত্তর
%dদিয়ে ছাপা হয় আর ওটা 1 বা 0। কোনো শব্দ না, কোনোifনা। - Statement যেখানে
unsignedবলে, সেখানে literal-র গায়েরu-টা উত্তরেরই অংশ।
একটা সংখ্যা কি আরেকটার গুণিতক, যখন প্রথমটা ঋণাত্মক হতে পারে?
#include <stdio.h>
int main(void)
{
int n = 0;
int d = 0;
scanf("%d %d", &n, &d);
printf("%d\n", n % d == 0);
return 0;
}
1
ওই output-টা -91 7 input-র জন্য। ঋণাত্মক একটা গুণিতকের ভাগশেষও ঠিক 0, তাই 0-র সাথে মেলানো রূপটাই মাইনাস চিহ্ন সামলে টিকে যায়।
নিচের তিনটা problem আসলে এই program-ই, শুধু bit নম্বর আলাদা।
#include <stdio.h>
int main(void)
{
unsigned int v = 0;
scanf("%u", &v);
v |= (1u << 2);
printf("%u\n", v);
v &= ~(1u << 0);
printf("%u\n", v);
v ^= (1u << 2);
printf("%u\n", v);
return 0;
}
5
4
0
ওই output-টা 1 input-র জন্য। 1-র bit 2 বসালে হয় 5, bit 0 মুছলে হয় 4, আর bit 2 আবার উল্টালে হয় 0।
নিচের একটা problem ঠিক এটাই চায়: নিজের কোনো bracket না, আর table-র নিজের উত্তর।
#include <stdio.h>
int main(void)
{
int a = 0;
int b = 0;
int c = 0;
scanf("%d %d %d", &a, &b, &c);
printf("%d\n", a * b % c);
printf("%d\n", a + b > c);
return 0;
}
-1
0
ওই output-টা -5 3 7 input-র জন্য। প্রথম লাইনটা আসলে (a * b) % c, আর -15 ভাগশেষ রাখে -1। দ্বিতীয়টা (a + b) > c, মানে -2-র সাথে 7।
এটা কোথায় কাজে লাগে
- একটা judge মানেই একটা contest। এই দশটার প্রতিটার গড়ন একটা contest problem-র গড়ন: input, output, constraints, লুকানো test। Progsity-র judge ওদের ঠিক সেভাবেই চালায় যেভাবে একটা contest round চালায়।
- Interview-র whiteboard। "এটা কি leap year" আর "একটা byte-র দুই অর্ধেক বদলাও", দুইটাই সত্যিকারের প্রথম রাউন্ডের প্রশ্ন, আর দুইটাই এক expression করে।
- Code review। "নিজের সীমার প্রান্তে গিয়ে এই লাইনটা কী করে", এটাই একজন reviewer-র প্রশ্ন, আর নিচের লুকানো test-গুলো তোমার হয়ে ওটা জিজ্ঞেস করে।
- Regression suite। এখানকার প্রতিটা test file একটা জোড়া: একটা input আর কারো হাতে বের করা output। প্রতিটা ভাষায় আর প্রতিটা কোম্পানিতে test মানে ঠিক এটাই।
যে ভুলগুলো সবাই করে
১. ভাগশেষকে একটা ধনাত্মক সংখ্যার সাথে মেলানো।
printf("%d\n", n % 2 == 1);
কোনো command line-এই বার্তা নেই। n -7 হলে ভাগশেষ -1, তাই এটা একটা বিজোড় সংখ্যার জন্য 0 বলে। 0-র সাথে মেলাও আর উল্টে নাও, নয়তো != 0 লেখো।
২. Mask-এ u ছাড়া লেখা।
v |= (1 << k);
প্রতিটা command line-এ নীরব। k 31 হলে এটা undefined behaviour, তাই এর কোনো output এখানে লেখা নেই। আটটা লুকানো test-র সাতটা তবু পাশ করবে, আর সেটাই এটাকে দামি করে তোলে।
৩. Packed মানের বদলে input থেকে খোলা।
printf("%u %u %u\n", r, g, b);
কোনো বার্তা নেই, আর প্রতিটা test পাশ করে। এটা কিছুই প্রমাণও করে না: program একটা bit-ও ফিরে পড়েনি। ঠিক এই জন্যই statement তোমাকে packed মান থেকে খুলতে বলে।
৪. যে problem bracket চায়নি সেখানে bracket যোগ করা।
printf("%d\n", a * (b % c));
কোনো বার্তা নেই, আর sample-টা পাশও করতে পারে। a * b % c মানে (a * b) % c, আর b c-র চেয়ে ছোট হলেই দুইটা আলাদা হয়ে যায়। Statement যে expression লিখেছে সেটাই টাইপ করো।
Maria-র locker room-এ প্রতিটা code ঠিক তিন অঙ্কের। ওর label printer অঙ্কগুলো একটা একটা করে চায়।
Input. এক লাইনে একটা পূর্ণসংখ্যা n।
Output. এক লাইনে n-র তিনটা অঙ্ক, মাঝে একটা করে space।
Constraints. 100 <= n <= 999।
Sample. Input 407 দিলে 4 0 7।
#include <stdio.h>
int main(void)
{
int n = 0;
scanf("%d", &n);
/* Hundreds with /, units with %, and the middle one needs both. */
return 0;
}
Run in Compiler
Hint 1
দুইটা operator-ই তিনটা অঙ্ক বের করে। / ডান দিক থেকে অঙ্ক ফেলে দেয়, % শেষেরটা রাখে।
Hint 2
শতকের অঙ্কটা একটা ভাগ। এককের অঙ্কটা একটা ভাগশেষ। দশকের অঙ্কটার জন্য আগে একটা ভাগ, তারপর একটা ভাগশেষ।
Solution
শতকের জন্য 100 দিয়ে ভাগ করো, কারণ integer division ওর ডানের সবটা ফেলে দেয়। এককের জন্য 10-র ভাগশেষ নাও, কারণ শেষ ভাগটা ঠিক ওই অংশটাই ফেলে দিত। দশকের জন্য আগে 10 দিয়ে ভাগ করো, যেটা দশকের অঙ্কটাকে এককের ঘরে নিয়ে আসে, তারপর 10-র ভাগশেষ নাও।
তিনটাই মূল n পড়ে, তাই তিন লাইনের ক্রমে কিছু আসে যায় না। 100 আর 500 দিয়ে পরীক্ষা করো: দুইটারই মাঝখানে একটা শূন্য, আর যে program ভাগশেষের বদলে বিয়োগে হাত বাড়িয়েছে সে প্রায়ই ওগুলো ভুল করে।
Zara একটা scheduler পরীক্ষা করছে যেটা প্রতি d tick পরপর একটা কাজ চালায়, আর একটা tick-র জন্য হ্যাঁ-না উত্তর চায়।
Input. এক লাইনে দুইটা পূর্ণসংখ্যা n আর d।
Output. এক লাইনে 1, যদি d n-কে পুরোপুরি ভাগ করে, নয়তো 0।
Constraints. -1000000000 <= n <= 1000000000, আর 1 <= d <= 1000000000।
Sample. Input 91 7 দিলে 1। Input -91 6 দিলে 0।
#include <stdio.h>
int main(void)
{
int n = 0;
int d = 0;
scanf("%d %d", &n, &d);
/* A comparison is worth 1 or 0, so print it with %d. No if needed. */
return 0;
}
Run in Compiler
Hint 1
"পুরোপুরি ভাগ করে" কথাটার আরেক মানে হলো "ভাগশেষে কিছুই পড়ে থাকে না"।
Hint 2
n ঋণাত্মক হতে পারে, আর lesson 1 বলেছে তাতে ভাগশেষের কী হয়। এমন একটা মান বাছো যেটা দুই চিহ্নেই একই থাকে।
Solution
পুরো program-টা একটা তুলনা: n-র d-র উপরের ভাগশেষ, 0-র সাথে মেলানো। ওই তুলনাটার মান আগে থেকেই 1 বা 0, তাই ওটা সরাসরি %d দিয়ে একটা printf-এ বসে যায় আর কোনো if লাগে না।
শূন্যই সেই মান যেটা ঋণাত্মক input-এও কাজ করে। ঋণাত্মক একটা গুণিতক ঠিক 0 ভাগশেষ রাখে, কোনো চিহ্নের ঝামেলা ছাড়াই, আর তুমি যে অন্য তুলনাগুলোতে হাত বাড়াতে পারো সেগুলো শূন্যের দুই পাশে আলাদা আচরণ করে।
Maria-র খাতা স্লিপে পাঁচটা বিক্রি নিয়ে বন্ধ হয়, আর ও একটা চলমান যোগফল চায়, একটা একটা করে বিক্রি যোগ করে।
Input. এক লাইনে পাঁচটা পূর্ণসংখ্যা। একটা বিক্রি ঋণাত্মক হতে পারে, কারণ ফেরতও একই স্লিপে ওঠে।
Output. এক লাইনে তাদের যোগফল।
Constraints. প্রতিটা মান -1000000 আর 1000000-র মধ্যে।
Sample. Input 10 20 30 40 50 দিলে 150।
#include <stdio.h>
int main(void)
{
int a = 0;
int b = 0;
int c = 0;
int d = 0;
int e = 0;
scanf("%d %d %d %d %d", &a, &b, &c, &d, &e);
int total = 0;
/* Five += lines, one per value. */
return 0;
}
Run in Compiler
Hint 1
পুরো পথ ধরে একটা বাক্সই উত্তরটা রাখে। পাঁচটা মানের প্রতিটা জায়গাতেই ওতে যোগ হয়।
Hint 2
Lesson 3-র +=-ই operator-টা। বাক্সটাকে 0 থেকে শুরু করতে হয়, আর starter সেটা তোমার হয়ে করে দিয়েছে।
Solution
যোগফলটা 0 থেকে declare করো, তারপর পাঁচটা compound assignment লেখো, প্রতি input-এ একটা। প্রতিটা লাইন যোগফলটার নাম একবার লেখে আর একটা মান ওতে যোগ করে, আর এই জন্যই এই রূপে "ভুল variable-র নাম লিখে ফেলা" bug-টার জায়গাই নেই, যেটা লম্বা রূপে আছে।
Type-টা এখানে ইচ্ছে করেই আরামদায়ক। এক মিলিয়ন করে পাঁচটা মান মানে পাঁচ মিলিয়ন, একটা int-র অনেক ভিতরে, তাই এই problem operator নিয়ে, চওড়া নিয়ে না। ঋণাত্মক test-গুলো আছে কারণ একটা ফেরত মাইনাস চিহ্নওয়ালা সাধারণ একটা বিক্রিই।
David সেই calendar পরীক্ষাটা লিখছে যেটা শেষ পর্যন্ত প্রতিটা project-এ লাগে। একটা বছর leap year, যদি সেটা 4 দিয়ে ভাগ যায় আর 100 দিয়ে না যায়, অথবা 400 দিয়ে ভাগ যায়।
Input. এক লাইনে একটা পূর্ণসংখ্যা y।
Output. এক লাইনে 1, যদি y leap year হয়, নয়তো 0।
Constraints. 1 <= y <= 999999।
Sample. Input 2024 দিলে 1। Input 1900 দিলে 0। Input 2000 দিলে 1।
#include <stdio.h>
int main(void)
{
int y = 0;
scanf("%d", &y);
/* Three remainders, two comparisons joined by &&, then one ||. */
return 0;
}
Run in Compiler
Hint 1
নিয়মটার ভিতরে তিনটা আলাদা প্রশ্ন আছে, আর প্রতিটাই 0-র সাথে মেলানো একটা ভাগশেষ।
Hint 2
নিয়মটা জোরে পড়ো আর তার গড়নটা নকল করো। "এটা আর ওটা না" মানে একটা &&; "অথবা অন্যটা" মানে একটা ||। Lesson 5 বলে && শক্ত করে ধরে, তাই bracket লাগে না।
Solution
তিনটা ভাগশেষের পরীক্ষা লেখো, প্রথম দুইটাকে && দিয়ে জোড়ো, তারপর ওই দলটাকে তৃতীয়টার সাথে || দিয়ে জোড়ো। পুরো নিয়মটাই একটা expression যার মান আগে থেকেই 1 বা 0, তাই ওটা একটা int-এ জমা হয় আর %d দিয়ে ছাপে।
1900 আর 2000, এই দুইটা বছরই বলে দেয় তুমি ঠিক লিখেছ কি না, আর দুইটাই লুকানো test-এ আছে। যে program "4 দিয়ে ভাগ যায়"-তেই থেমে যায় ও 1900-র জন্য 1 বলে, আর যে শেষ শর্তটা ভুলে যায় ও 2000-র জন্য 0 বলে। Test-র বাকি প্রতিটা বছরে তিনটা রূপই মিলে যায়।
Kenji দেখাতে চায় ও একটা লাইন C যেভাবে পড়ে সেভাবে পড়তে পারে। ওর শিক্ষক ওকে পাঁচটা expression আর তিনটা সংখ্যা দেন।
Input. এক লাইনে তিনটা পূর্ণসংখ্যা a, b আর c।
Output. পাঁচ লাইন, a + b * c, a - b - c, a * b % c, a < b && b < c আর a + b > c-র মান, এই ক্রমে।
Constraints. -1000 <= a, b <= 1000, আর 1 <= c <= 1000।
Sample. Input 2 3 4 দিলে পাঁচ লাইন 14, -5, 2, 1, 1।
#include <stdio.h>
int main(void)
{
int a = 0;
int b = 0;
int c = 0;
scanf("%d %d %d", &a, &b, &c);
/* Type the five expressions exactly as written. Add no brackets. */
return 0;
}
Run in Compiler
Hint 1
বের করার মতো কিছুই নেই। Statement যেভাবে লিখেছে সেভাবেই পাঁচটা expression টাইপ করো আর প্রতিটা %d দিয়ে ছাপো।
Hint 2
ফাঁদটা হলো সাহায্য করার ইচ্ছা। তুমি ভালোর জন্য একটা bracket যোগ করলে লাইনটার মানেই বদলে যায়, আর লুকানো test মূলটাই মাপে।
Solution
পাঁচটা printf লাইন, প্রতিটায় পাঁচটার একটা expression হুবহু লেখা, সবই %d দিয়ে, কারণ একটা তুলনাও একটা int। উত্তরগুলো lesson 5-র table থেকেই আসে: +-র আগে *, - বাঁ থেকে ডানে দল বাঁধে, * আর % একই সারিতে আর বাঁ থেকে ডানে বাঁধে, &&-র আগে তুলনা, আর তুলনার আগে অঙ্ক।
তৃতীয় লাইনেই লুকানো test কামড় দেয়। ওটার মানে গুণফলের ভাগশেষ, তাই a * b ঋণাত্মক হলে উত্তরটাও ঋণাত্মক, আর যে program আগে ভাগশেষ বের করত সে একই input-এ অন্য সংখ্যা দিত।
David এমন একটা calculator লিখছে যা কেউ কখনো এত ছোট বানায়নি। ওটা পাঁচটা সংখ্যা পড়ে আর a * b + c / d - e % d-র মান বের করে, ঠিক C যেভাবে করত।
Input. এক লাইনে পাঁচটা পূর্ণসংখ্যা a, b, c, d আর e।
Output. এক লাইনে ওই expression-টার মান।
Constraints. -1000 <= a, b, c, e <= 1000, আর 1 <= d <= 1000।
Sample. Input 2 3 10 4 7 দিলে 5।
#include <stdio.h>
int main(void)
{
int a = 0;
int b = 0;
int c = 0;
int d = 0;
int e = 0;
scanf("%d %d %d %d %d", &a, &b, &c, &d, &e);
/* One expression, typed exactly as the statement writes it. */
return 0;
}
Run in Compiler
Hint 1
পাঁচটা operator-র তিনটাই lesson 5-র table-র 3 নম্বর সারিতে, তাই + আর - পালা পাওয়ার আগেই ওরা দল বেঁধে ফেলে।
Hint 2
c / d আর e % d দুইটাই ঋণাত্মক হতে পারে, আর প্রতিটা তার নিজের বাঁ পাশ থেকে চিহ্ন নেয়। কিছু টাইপ করার আগে ঋণাত্মক একটা e নিয়ে sample-টা কাগজে বের করো।
Solution
একটা printf, ভিতরে expression-টা যেমন লেখা তেমনই। C ওটাকে দল বাঁধায় এভাবে: গুণফল, যোগ ভাগফল, বিয়োগ ভাগশেষ, কারণ *, / আর % এমন এক precedence সারিতে আছে যেটা + আর --র উপরে বসে। এখানে কোনো bracket কিছুই বদলায় না, আর সেই জন্যই কোনোটা লাগেও না।
চিহ্নওয়ালা দুই টুকরাই পুরো problem। ঋণাত্মক c ভাগটাকে নিচের দিকে না, শূন্যের দিকে কাটায়, আর ঋণাত্মক e ভাগশেষটাকে ঋণাত্মক করে, তাই ওটা বিয়োগ করা মানে যোগ করা। সবচেয়ে বড় মানগুলোও একটা int-র অনেক ভিতরে: এক হাজার গুণ এক হাজার মানে দশ লাখ।
Maria-র image tool একটা রং তিনটা byte হিসেবে এক সংখ্যায় ভরে রাখে, তারপর ওগুলো আবার আলাদা করতে হয়।
Input. এক লাইনে তিনটা পূর্ণসংখ্যা r, g আর b।
Output. দুই লাইন: packed মান, যেখানে লাল bit 16 থেকে 23-এ, সবুজ 8 থেকে 15-এ আর নীল 0 থেকে 7-এ; তারপর ওটা থেকে ফিরে পড়া r, g আর b, মাঝে একটা করে space।
Constraints. 0 <= r, g, b <= 255।
Sample. Input 200 130 40 দিলে 13140520, তারপর 200 130 40।
#include <stdio.h>
int main(void)
{
unsigned int r = 0;
unsigned int g = 0;
unsigned int b = 0;
scanf("%u %u %u", &r, &g, &b);
/* Pack with two shifts and two ors. Unpack with shifts and 255u. */
return 0;
}
Run in Compiler
Hint 1
প্রতিটা মান বড়জোর 255, তাই প্রতিটা আটটা bit নেয়, আর একবার সরিয়ে দিলে কোনো দুইটা একটার উপর আরেকটা পড়ে না।
Hint 2
ভরা মানে | দিয়ে জোড়া দুইটা shift। খোলা মানে ওই shift-গুলোই উল্টো দিকে, প্রতিটার পরে 255u দিয়ে একটা mask।
Solution
লালকে 16 ঘর উপরে সরাও, সবুজকে 8 ঘর, নীল যেখানে আছে সেখানেই রাখো, আর তিনটাকেই | দিয়ে জোড়ো। প্রতিটা মান আট bit-এ আঁটে বলেই তিনটা টুকরা 32 bit-র সংখ্যাটায় পাশাপাশি বসে আর কিছুই হারায় না। একটা ফিরে পড়তে ওটাকে একদম নিচে নামাও আর 255u দিয়ে mask করো, যেটা তোমার চাওয়া আট bit-র উপরের সব ফেলে দেয়।
তিনটা input থেকে না, packed মান থেকেই খোলো। Input আবার ছাপলে প্রতিটা test পাশ করে আর কিছুই প্রমাণ হয় না, কারণ program একটা bit-ও ফিরে পড়ত না। আগ্রহের test তিনটা সেগুলো যেখানে ঠিক একটা channel শূন্য না: ওরা উল্টো দিকে লেখা একটা shift ধরে ফেলে।
Amara পুরনো একটা যন্ত্রের format পড়ছে, যেখানে প্রতিটা byte-র চার bit-র দুই অর্ধেক উল্টো হয়ে এসেছিল।
Input. এক লাইনে একটা পূর্ণসংখ্যা b।
Output. এক লাইনে b-র মান, যেখানে উপরের চার bit আর নিচের চার bit জায়গা বদল করেছে।
Constraints. 0 <= b <= 255।
Sample. Input 195 দিলে 60। Input 16 দিলে 1।
#include <stdio.h>
int main(void)
{
unsigned int b = 0;
scanf("%u", &b);
/* Two halves: one moves up four places, the other moves down four. */
return 0;
}
Run in Compiler
Hint 1
চার bit মানে 15u mask, যেটা 1111। দুইটা অর্ধেকেরই ওটা লাগে, আলাদা কারণে।
Hint 2
নিচের অর্ধেকটা নাও, mask করো, তারপর চার ঘর উপরে সরাও। উপরের অর্ধেকটা চার ঘর নিচে সরিয়ে নাও, তারপর mask করো। দুইটাকে | দিয়ে জোড়ো।
Solution
নিচের চার bit-কে 15u দিয়ে mask করো আর ফলটাকে চার ঘর উপরে সরাও। পুরো মানটাকে চার ঘর নিচে সরাও আর ওটাকেও 15u দিয়ে mask করো। দুইটা ফল একটা byte-র আলাদা দুই অর্ধেকে বসে, তাই | ওদের উত্তরে জুড়ে দেয়।
নিচের অর্ধেকের mask-টাই আটকায় যাতে উত্তরটা এমন bit না বয়ে আনে যা কখনো ওর অংশ ছিল না। এই input-র সীমায় মানটা মাত্র আট bit চওড়া, তাই ওই mask ছাড়া program-টা কাকতালীয়ভাবে ঠিক হয়ে যায়, আর একটু চওড়া কিছুতে হতো না। Mask লেখাটাই সেই অভ্যাস যা পরের problem-এ টিকে থাকে।
Kenji-র control board এক সংখ্যায় 32টা সুইচ রাখে। একটা রক্ষণাবেক্ষণের নির্দেশ একটা সুইচ চালু করতে, একটা বন্ধ করতে আর একটা উল্টাতে বলে।
Input. এক লাইনে চারটা পূর্ণসংখ্যা: শুরুর মান v, তারপর bit নম্বর i, j আর k।
Output. তিন লাইন: bit i বসানোর পরের মান, তারপর bit j মুছে দেওয়ার পরের, তারপর bit k উল্টে দেওয়ার পরের। প্রতিটা ধাপ আগেরটার রেখে যাওয়া মান থেকে শুরু হয়।
Constraints. 0 <= v <= 4294967295, আর 0 <= i, j, k <= 31।
Sample. Input 0 3 3 5 দিলে তিন লাইন 8, 0, 32।
#include <stdio.h>
int main(void)
{
unsigned int v = 0;
unsigned int i = 0;
unsigned int j = 0;
unsigned int k = 0;
scanf("%u %u %u %u", &v, &i, &j, &k);
/* Three idioms, in order: set, clear, toggle. Print after each one. */
return 0;
}
Run in Compiler
Hint 1
তিনটা idiom lesson 4-এ আছে, একই ক্রমে। প্রতিটাই একই variable-র উপর একটা compound assignment, তারপর একটা printf।
Hint 2
Constraints bit নম্বর 31 পর্যন্ত দেয়। ওই একটা মানই ঠিক করে দেয় তোমার mask-র literal-এ কোন অক্ষরটা লাগবে।
Solution
তিন লাইন, statement যে ক্রমে বলেছে সেই ক্রমে: বসাতে mask-র সাথে |, মুছতে উল্টানো mask-র সাথে &, উল্টাতে mask-র সাথে ^। প্রতিটাই compound assignment, তাই মানটা সামনে বয়ে যায়, আর প্রতিটার পরে একটা printf board যেভাবে জানায় সেভাবেই অবস্থাটা জানায়।
প্রতিটা mask 1u সরানো, কখনো 1 না। Bit নম্বর 31 হলে signed একটা 1 চিহ্নের bit-এ গিয়ে পড়ত, যেটা undefined behaviour আর যেটা নিয়ে Playground কিছুই বলে না। Clear idiom-টাই একমাত্র যেটায় ~ লাগে, কারণ ওর দরকার এমন একটা mask যেটা এক ঘরে শূন্য আর বাকি সব ঘরে এক।
Maria-র stock import-র পরে কিছু গোনা ঋণাত্মক হয়ে গেছে, আর সীমাটা unsigned int-এ রাখা একটা পরীক্ষা ওদের প্রতিটার জন্যই না বলে আসছে।
Input. এক লাইনে দুইটা পূর্ণসংখ্যা: signed একটা count আর একটা limit, যেটা তোমার program-কে একটা unsigned int-এ পড়তে হবে।
Output. দুই লাইন: সীমাটা unsigned রেখে count < limit-র মান, তারপর ওই একই তুলনার মান signed অঙ্কে করা।
Constraints. -1000000 <= count <= 1000000, আর 0 <= limit <= 1000000।
Sample. Input -1 1 দিলে 0, তারপর 1।
#include <stdio.h>
int main(void)
{
int count = 0;
unsigned int limit = 0;
scanf("%d %u", &count, &limit);
/* First line: no cast. Second line: one cast on the limit. */
return 0;
}
Run in Compiler
Hint 1
প্রথম লাইনটাই bug, ইচ্ছে করে লেখা। ওটা ঠিক কোরো না; statement দুইটা উত্তরই চায়।
Hint 2
সীমাটা বড়জোর দশ লাখ, তাই ওটা একটা int-এ আরামেই আঁটে। ডান পাশের মানে একটা cast পুরো তুলনাটাকে lesson 6-র মইয়ের এক ধাপ নিচে নামায়।
Solution
প্রথম লাইনটা দুইটাকে যেমন আছে তেমনই মেলায়। মই count-কে unsigned int-এ তোলে, যেখানে ঋণাত্মক একটা মান খুব বড় একটা ধনাত্মক হয়ে যায়, তাই প্রতিটা ঋণাত্মক গোনার উত্তর 0। ওই রূপান্তরটা cast হিসেবে লিখে রাখলে ফল বদলায় না, আর task-র চাওয়া একটা রূপান্তর নিয়ে স্থানীয় gcc -Wall-র নালিশটাও থামে।
দ্বিতীয় লাইন সীমাটাকে int-এ cast করে। তখন দুই পাশই signed, মইয়ের তোলার কিছু থাকে না, আর তুলনাটার মানে যা পড়ায় তাই। ওই cast এখানে নিরাপদ শুধু এই কারণে যে constraints কথা দেয় সীমাটা একটা int-এ আঁটে, আর যে program উল্টো দিকে cast করে, ঋণাত্মক হতে পারে এমন একটা signed মান থেকে, ও bug-টা সারানোর বদলে আরও খারাপ করে।
সচরাচর যে প্রশ্নগুলো আসে
ifআমি আগেই জানি, তবু ব্যবহার করা যাবে?যাবে, আর judge মেনেও নেবে। তবে তুমি আসল কথাটা এড়িয়ে গেলে: এই প্রতিটাই এক expression, আর সেটা দেখতে পাওয়াই Module 5-কে সহজ করে দেয়।
Bit-র problem-গুলো
%u-তে জোর দেয় কেন?কারণ constraints-এ bit নম্বর 31 আছে। মানটা signed একটা
int-এ পড়লে তার চারপাশের shift-গুলো আর defined থাকে না।আমার program sample-এ ঠিক আর একটা লুকানো test-এ পড়ে যায়। কোথায় দেখব?
চিহ্নে। এই দশটার চারটা ঋণাত্মক input নেয়, আর তার তিনটায় চিহ্নটা একটা
/বা%-র উত্তর বদলে দেয়।five-valuesআরexpr-eval-এ কি bracket লাগবে?না, আর ওগুলো যোগ করলে উত্তর বদলে যায়। দুইটা problem-ই মাপছে table কী বলে, কোনটা পড়তে সুন্দর লাগে তা না।
Hint ladder কি আমার বিপক্ষে গোনা হয়?
না। একটা hint খোলা লেখা থাকে আর তাতে কিছুই খরচ হয় না। তারপর program-টা নিজে টাইপ করো, কারণ পড়াটা দক্ষতা না।
মূল কথা
- আগে Constraints পড়ো আর যেখানে যেখানে ঋণাত্মক মান চলতে পারে দাগিয়ে রাখো।
- ভাগশেষকে 0-র সাথে মেলানো হয়, কারণ তার চিহ্ন বাঁ পাশের মান ধরে।
- হ্যাঁ-না উত্তর একটা তুলনা,
%dদিয়ে 1 বা 0 হিসেবে ছাপা। - Bit নিয়ে কাজে
unsignedমান,%u, আর প্রতিটা mask literal-এ একটাu। - Statement যে expression লিখেছে সেটা যেমন আছে তেমনই টাইপ হয়; তোমার যোগ করা bracket একটা পরিবর্তন।
- Cast বসে মানের গায়ে, আর কেবল সেখানেই যেখানে constraints প্রমাণ করে ওটা নিরাপদ।
এরপর module test, আর তার পরে Module 5, যেখানে একটা তুলনা অবশেষে ঠিক করতে পারবে কোন লাইনগুলো চলবে।
lesson ৭ শেষ
সব problem accepted হলেই lesson শেষ।
১০ টা problem-এর মধ্যে ০ টা accepted
পরেরটা: Module test: operator