CECP004 - রাশিচক্র


রাশিচক্রের সাথে সবাই কমবেশি পরিচিত। মানুষের জন্ম তারিখের উপর ভিত্তি করে তার রাশি নির্ধারণ করা হয়। কারো জন্ম যদি একটি নির্দিষ্ট তারিখ রেঞ্জের মধ্যে হয়, তাহলে তাকে একটি নির্দিষ্ট রাশির জাতক/জাতিকা বলা হয়। যেমন, জন্ম যদি

১৬ এপ্রিল - ১৫ মে হয়, রাশি হবে Aries বা মেষ,
১৬ মে - ১৫ জুন হয়, রাশি হবে Taurus বা বৃষ,
১৬ জুন - ১৫ জুলাই হয়, রাশি হবে Gemini বা মিথুন,
১৬ জুলাই - ১৫ আগষ্ট হয়, রাশি হবে Cancer বা কর্কট,
১৬ আগষ্ট - ১৫ সেপ্টেম্বর হয়, রাশি হবে Leo বা সিংহ,
১৬ সেপ্টেম্বর - ১৫ অক্টোবর হয়, রাশি হবে virgo বা কন্যা,
১৬ অক্টোবর - ১৫ নভেম্বর হয়, রাশি হবে Libra বা তুলা,
১৬ নভেম্বর - ১৫ ডিসেম্বর হয়, রাশি হবে Scorpio বা বৃশ্চিক,
১৬ ডিসেম্বর - ১৫ জানুয়ারি হয়, রাশি হবে Sagittarius বা ধনু,
১৬ জানুয়ারি - ১৫ ফেব্রুয়ারি হয়, রাশি হবে Capricorn বা মকর,
১৬ ফেব্রুয়ারি - ১৫ মার্চ হয়, রাশি হবে Aquarius বা কুম্ভ,
১৬ মার্চ - ১৫ এপ্রিল হয়, রাশি হবে Pisces বা মীন।

Tûshar কিছুদিন হল রাশিচক্র সম্পর্কে জেনেছে। এখন সে সবাইকে তাদের জন্মতারিখ জিজ্ঞেস করে এবং এই তালিকা থেকে তার রাশি বের করে।

আপনাকে এমন একটি প্রোগ্রাম তৈরী করতে হবে যাতে তুষার একটি জন্মতারিখ ইনপুট হিসেবে দিলে, আউটপুট হিসেবে রাশির নাম পাবে।

ইনপুট

দিন-মাস-বছর এই ভাবে জন্মতারিখ ইনপুট দেওয়া হবে। যেমন, ০১ জানুয়ারি ২০০১ যদি কারো জন্মতারিখ হয় তাহলে ইনপুট হবে, 01-01-2001।

আউটপুট

নির্ভুল জন্মতারিখের জন্য উপরের তালিকায় দেওয়া রেঞ্জের রাশির নামটি আউটপুট দেওয়া হবে। যদি জন্মতারিখ ত্রুটিপূর্ণ হয় তাহলে আউটপুট হবে "Not Possible"।

sample input 1
15-09-1986

sample output 1
Leo

sample input 2
29-02-2016

sample output 2
Aquarius

sample input 3
31-09-1999

sample output 3
Not Possible


প্রব্লেম সেটার: হাসিবুল হাসান হাওলাদার

Array : সাধারণ আলোচনা

Array শব্দটি ইংরেজিতে ব্যবহার করা হয় ধারাবাহিক ভাবে সজ্জিত একটি সমাবেশকে বুঝানোর জন্য। যেমন, array of computer workstations বলতে বুঝায় পাশাপাশি সাজানো অনেকগুলি ডেস্কটপ কম্পিউটার এর একটি সমাবেশকে, ঠিক যেমন স্কুল, কলেজ বা ইউনিভার্সিটির কম্পিউটার ল্যাবে সাজানো থাকে।

প্রোগ্রামিং ল্যাংগুয়েজ এর পরিভাষায়, array হচ্ছে একই ডেটা টাইপের অনেকগুলো ডেটার একটি ধারাবাহিক সমাবেশ।

উপরের সংগা থেকে, কয়েকটা জিনিস স্পষ্ট যে,
১. array হচ্ছে একই ডেটা টাইপের ডেটার সমাবেশ
২. এই সমাবেশে বেশ কিছু ডেটা থাকবে।
৩. এই ডেটা সমূহ ধারাবাহিক ভাবে পাশাপাশি কম্পিউটার মেমরিতে সাজানো থাকে।

আমরা জানি, C ভাষায় যে কোন ডেটা কম্পিউটার মেমরিতে রাখার জন্য ভ্যারিয়েবল ডিক্লেয়ার করতে হয়। array হচ্ছে একই ধরনের অনেকগুলি ভ্যারিয়েবল এর মত।

একটি array কম্পিউটার মেমরিরে তার ডেটা টাইপ অনুযায়ী একটি নির্দিষ্ট পরিমান জায়গা দখল করে থাকে। এই জায়গার পরিমান তার ডেটা টাইপ ও আমরা কতগুলো ডেটা ঐ array তে রাখতে চাই তার উপর নির্ভর করে।

array ব্যবহার করা হয়, একই ধরনের ডেটা গুলোকে তালিকা আকারে সাজিয়ে রাখার জন্য। ধারাবাহিকভাবে সাজানো থাকে বলে এই তালিকা থেকে একটি নির্দিষ্ট ডেটাকে সহজে খুজে পাওয়া যায়, এবং সহজে ব্যবহার করা যায়।

ডিক্লেয়ারেশন


সাধারণ ভ্যারিয়েবল এর মতই array ডিক্লেয়ার করার সময় প্রথমে তার ডেটা টাইপ উল্লেখ করতে হয় এবং তারপর তার নাম দিতে হয়। তবে নামের সাথে বিশেষ পদ্ধতিতে, ঐ array তে কতগুলি ডেটা থাকবে সেটাও উল্লেখ করে দিতে হয়। নামের সাথেই থার্ড ব্রাকেট '[]' এর মধ্যে ঐ array এর উপাদান সংখ্যা নির্ধারণ করে দিতে হয়। যেমন, আমরা যদি ১০০ জন ছাত্রছাত্রীর পরীক্ষায় প্রাপ্ত নম্বর রাখার জন্য একটি array ডিক্লেয়ার করতে চাই, তাহলে আমাদের int টাইপের একটি array ডিক্লেয়ার করতে হবে যার উপাদান সংখ্যা হবে ১০০। যদি array টির নাম marks হয়, তাহলে ডিক্লেয়ারেশন স্টেটমেন্ট হবে,

int marks[100];

খেয়াল রাখতে হবে যে নাম আর [ এর মধ্যে যেন কোন স্পেস বা ফাকা জায়গা না থাকে।

উপরের marks array টি ১০০ টি ইন্টিজার ভ্যারিয়েবল এর সমাবেশ হিসেবে কাজ করে।

এখন আমরা ইচ্ছে করলে এই array এর একটি একটি করে ডেটা বা উপাদানকে এক্সেস করতে পারবো। array এর একটি উপাদানকে এক্সেস করার পদ্ধতিকে ইন্ডেক্সিং বলে। আর ঐ array তে ঐ উপাদানের ক্রমিক নম্বরকে তার ইন্ডেক্স নম্বর বলে। ইন্ডেক্স নম্বর শুরু হয় 0 (শূন্য) থেকে। কম্পিউটার বিজ্ঞানে গণনা সাধারণত শূন্য থেকেই শুরু হয়। কাজেই, কোন array এর প্রথম উপাদানের ইন্ডেক্স নম্বর হবে শূন্য। আর শেষ উপাদানের ইন্ডেক্স নম্বর হবে array এর আকারের চেয়ে ১ কম। উপরের marks array তে  0 থেকে 99 পর্যন্ত মোট ১০০ টি ইন্ডেক্স আছে।

array এর নামের পরে থার্ড ব্রাকেট এর মধ্যে ইন্ডেক্স নম্বর দিয়ে ঐ ইন্ডেক্সে রক্ষিত ডেটা এক্সেস করা যায়। যেমন,

marks[0] বলতে বোঝায় প্রথম ইন্ডেক্সে রক্ষিত ডেটাকে, marks[50] বলতে বোঝায় ৫১তম ইন্ডেক্সে রক্ষিত ডেটাকে।

আমরা যদি এই array টির ক্ষেত্রে marks[100] লিখি, তাহলে সেটা ভুল হবে। কারণ এই array তে ১০০ নম্বর ইন্ডেক্স বলে কিছু নেই। তাই সেটি এক্সেস করাও সম্ভব না।

array নিয়ে কাজ করার সময় সর্বদা সতর্ক থাকতে হবে যেন ইন্ডেক্সিং সঠিকভাবে করা হয়। অন্যথায় প্রোগ্রাম ক্রাশ করবে।

ইনিশিয়ালাইজেশন


অন্যান্য ভ্যারিয়েবল এর মত ডিক্লেয়ারেশন এর সময় array কে এসাইনমেন্ট অপারেটর ব্যবহার করে ইনিশিয়ালাইজ করা যায়। এই ক্ষেত্রে একটি কার্লি ব্রেসের মধ্যে কমা দিয়ে আলাদা করা ডেটার তালিকা array তে এসাইন করলেই ঐ ডেটা ধারাবাহিকভাবে array এর বিভিন্ন ইন্ডেক্সে একই ক্রমে কপি হয়ে যায়।

যেমন,

int num[5]={1, 2, 3, 4, 5};

স্টেটমেন্ট টি num নামে ৫ ইন্ডেক্সের একটি ইন্টিজার array ডিক্লেয়ার করে এবং তার 0 থেকে 4 নম্বর ইন্ডেক্সে যথাক্রমে 1, 2, 3, 4, 5 জমা রাখে।

array কে মোট ইন্ডেক্স সংখ্যা ছাড়া ডিক্লেয়ার করা যায় না। যেমন,

int num[];

ডিক্লেয়ারেশন স্টেটমেন্ট টি ভুল।

তবে যদি ডিক্লেয়ার করার সময় ইনিশিয়ালাইজ করা হয় তাহলে array কে মোট ইন্ডেক্স নম্বর ছাড়াও ডিক্লেয়ার করা যায়।

যেমন,

int num[]={1, 2, 3, 4, 5, 6};

স্টেটমেন্টটি সঠিক। এই ক্ষেত্রে কম্পাইলার array টিকে ইনিশিয়ালাইজ করার সময় কার্লি ব্রেস এর মধ্যে থাকা মোট ডেটার সংখ্যাকে ঐ array এর মোট ইন্ডেক্স সংখ্যা হিসেবে নির্ধারণ করে দেয়।


এসাইনমেন্ট অপারেটর ও array


 array একটি ভ্যারিয়েবল সমাবেশ মাত্র, এটি নিজে একটি ভ্যারিয়েবল নয়। তাই, দুটি array একই টাইপের ও একই আকারের হলেও একটি array কে অন্য array তে এসাইন করা যায় না। যেমন,

int num[10], odd[10];

odd=num; /* এই স্টেটমেন্টটি ভুল */

array এর একটি ইন্ডেক্স একটি ভ্যারিয়েবল এর মত কাজ করে। তাই একটি একটি করে ইন্ডেক্স এসাইনমেন্ট অপারেটর ব্যবহার করে কপি করা যায়। যেমন,

int num[5], odd[5];

num[0]=odd[0];
num[1]=odd[1];

এভাবে এসাইন করা যায়।

একটি array এর সব তথ্য অন্য একটি array তে এসাইনমেন্ট অপারেটর ব্যবহার করে কপি করতে চাইলে, একটি একটি করে সবগুলো ইন্ডেক্স এসাইন করতে হবে। এই ক্ষেত্রে দ্বিতীয় array টিকে অবশ্যই প্রথম array এর সমান বা তার চেয়ে বড় হতে হবে। কারণ, আগেই বলা হয়েছে যে ইন্ডেক্স যে array তে নাই, সেই ইন্ডেক্সে এসাইন করা বৈধ নয়।

লিনিয়ার বা সিকোয়েন্সিয়াল সার্চ

লিনিয়ার বা সিকোয়েন্সিয়াল সার্চ একেবারে মৌলিক একটি সার্চ এলগোরিদম। নাম অনুযায়ীই এটি একটি ডেটা স্ট্রাকচারের উপাদানগুলিকে পর্যায় ক্রমে খুঁজে খুঁজে কাংক্ষিত উপাদানটি খুজে বের করে। অবিন্যস্ত ডেটা বা এমন ডেটা যা কোন নির্দিষ্ট ক্রমে সজ্জিত নয়, তার ক্ষেত্রে এটিই একমাত্র সার্চ টেকনিক তবে এটি বেশ সময় সাপেক্ষ।



লিনিয়ার সার্চের মূল ধারণাটি খুব সহজ। আমরা একদম প্রথম উপাদান থেকে খোজা শুরু করবো। এবং যতক্ষন কাংক্ষিত উপাদানটি খুজে পাওয়া না যায় ততক্ষণ খোজ চলতে থাকবে।
লিনিয়ার সার্চ

আমাদের পরিচিত ডেটা স্ট্রাকচার array দিয়েই আমরা উদাহরণটি দিই। ধরা যাক, ১২ টি ভিন্ন সংখ্যা একটি array তে রাখা আছে। আমরা এই array তে লিনিয়ার সার্চ চালিয়ে দেখবো যে এখানে 25 সংখ্যাটি আছে কি না। ধরি, এই array তে সংখ্যাটি নেই। সেই ক্ষেত্রে 0 নম্বর ইন্ডেক্স থেকে 11 নম্বর ইন্ডেক্স পর্যন্ত সার্চ চলবে।



আবার ধরা যাক একটি array তে ১৫ টি ইন্ডেক্স এ 1-15 পর্যন্ত সংখ্যাগুলো আছে। আমাদের সেখান থেকে 9 কে খুজে বের করতে হবে। এই ক্ষেত্রেও আমরা 0 নম্বর ইন্ডেক্স থেকে খোজা শুরু করবো, এবং 8 নম্বর ইন্ডেক্স পর্যন্ত সার্চ চালিয়ে গেলে আমরা আমাদের কাংক্ষিত সংখ্যা 9 খুজে পাবো।


আমাদের কাজের ধাপ হবেঃ

১. 0 ইন্ডেক্স এ খুজে দেখা হবে।
২. যদি কাংক্ষিত সংখ্যা খুজে না পাওয়া যায় এবং এটিই শেষ ডেটা উপাদান না হয় তাহলে ইন্ডেক্স সংখ্যা ১ বৃদ্ধি পাবে এবং আবার ১ নং ধাপে ফেরত যাওয়া হবে। এটিই শেষ ডেটা উপাদান হলে সার্চ থেমে যাবে এবং খুজে পাওয়া যায় নি এমন মেসেজ দেওয়া হবে। যদি কাংক্ষিত সংখ্যা খুজে পাওয়া যায় তাহলেও সার্চ থেমে যাবে এবং খুজে পাওয়া গিয়েছে এমন মেসেজ দেওয়া হবে।

এইবার একটি সহজ কোড দেখি। ধরি array[20] একটি array যেখানে ২০ টি সংখ্যা আছে এবং আমাদের খুজে বের করতে হবে যে কত নম্বর ইন্ডেক্সে 75 সংখ্যাটি আছে।

#include<stdio.h>

int main()
{
    int i,array[20] = {2,3,5,6,33,45,6,34,57,55,75,89,4,6,7,86,54,32,99,78};

    for (i=0; i<20; i++)
    {
        if (array[i]==75)
        {
            printf("Found at index %d", i);
            break;
        }
     }
     if (i==20)
         printf("Not found in any index.");

    return 0;
}

একটি বাক্য থেকে শব্দ গণনা

ধরা যাক আমাদেরকে বলা হল যে, একটা ইংরেজি বাক্য দেওয়া আছে। আমাদের গুনে বের করতে হবে যে বাক্যটিতে কয়টি শব্দ আছে। আমরা চোখ দিয়ে তাকিয়ে শব্দ গুনে ফেলতে পারবো, কোন সমস্যা নয়। কিন্তু আমাদের যদি একটি প্রোগ্রাম লিখতে হয় এই গোনার কাজটি করার জন্য, তখন আমরা কি করব?

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

এইবার শব্দ খোজার পালা। আর এখানেই আমাদের দরকার লিনিয়ার বা সিকোয়েন্সিয়াল সার্চ সম্পর্কে ধারণা। যাদের এই ধারণাটা নাই তারা দয়া করে এখান থেকে পড়ে আসেন

লিনিয়ার সার্চের মতই আমরা স্ট্রিং এর প্রথম ইন্ডেক্স থেকে খোজা শুরু করবো এবং শেষের নাল টার্মিনেটর ধারী ইন্ডেক্স পর্যন্ত খুঁজবো। এখন প্রশ্ন হল, আমরা কোন বৈশিষ্ট্য টা ধরে খুজবো? কিসের উপর ভিত্তি করে খুজলে আমরা শব্দের সঠিক সংখ্যা খুজে পাবো? আসুন আমরা একটা উদাহরণ দিই। ধরি,

I love you.

হল আমাদের ইংরেজি বাক্য। আমরা জানি বাক্যের ২ টি শব্দের মধ্যে একটি ফাকা স্থান বা blank স্পেস থাকে। কাজেই আমরা যদি ফাকা স্থানের সংখ্যা বের করি তাহলে শব্দ হিসাব করাটা কিছুটা সহজ হবে। যেমন, উপরের উদাহরণ এ ২ কি ফাকা স্থান আছে। আর, ব্যাকরণগত ভাবে সঠিক যে কোন বাক্যের শেষে একটি ফুলস্টপ থাকবে এবং তার আগে একটি শব্দ থাকবে। তাই স্পেস সংখ্যার সাথে ১ যোগ করলেই আমরা পেয়ে যাবো শব্দ সংখ্যা।

কিন্তু সমস্যা অন্য এক জায়গায়। যদি বাক্যটি ব্যাকরণগত ভাবে সঠিক না হয়? যদি দুইটি শব্দের মধ্যে একাধিক ফাকা স্থান থাকে? যদি বাক্যের শেষে ফুল স্টপ এর পরিবর্তে ফাকা স্থান থাকে? যদি বাক্যের শুরুতেই একটি স্পেস থাকে? এরকম অনেক অনেক সমস্যায় আমাদের পড়তে হতে পারে। কাজেই যদি বলা না থাকে যে ব্যাকরণগত ভাবে সঠিক বাক্য আমাদের দেওয়া হবে সেই ক্ষেত্রে আমাদেরকে এই সব ত্রুটিপূর্ণ অবস্থাগুলোর কথা বিবেচনায় রাখতে হবে। আমাদেরকে এমন একটি অবস্থার কথা চিন্তা করতে হবে, এমন একটি শর্ত বিবেচনা করতে হবে, যে শর্তে গণনা করলে বাক্যের গঠন যেমনই হোক না কেন সঠিক শব্দসংখ্যা আমরা পেয়ে যাবো।

আমি একটি শর্ত সাজেস্ট করি।

১. আমরা একেবারে শুরুর ইন্ডেক্স থেকে গননা শুরু করবো না। আমরা ১ নম্বর ইন্ডেক্স থেকে গননা শুরু করবো।
২. আমরা তিনটা জিনিসের দিকে নজর রাখবো। স্পেস, স্পেশাল ক্যারেকটার আর নাল টার্মিনেটর।

৩. এদের যে কোন একটাকে খুঁজে পেলে চেক করে দেখবো যে তার আগের ইন্ডেক্সে একটি এলফাবেট বা নাম্বার আছে কি না। যদি এলফাবেট থাকে তাহলে আমরা ধরে নিবো যে একটা শব্দ (হিসাবে সুবিধার জন্য আমরা ধরে নিচ্ছি যে সংখ্যা বা সংখ্যাযুক্ত শব্দও শব্দ) আমরা পেয়ে গেছি। আর যদি তা না হয় তাহলে আমরা পরবর্তি ইন্ডেক্সে সার্চ চালিয়ে যাবো।

কাজেই আমাদের কোড এর গঠন হবে,

#include<stdio.h>
#include<ctype.h>

int main()
{
    char str[128],  ch; /* স্ট্রিং ধারণের জন্য array */
    int i, word=0;

    /* ইনপুট অংশ শুরু */
    for (i =0;;)
    {
        ch=getchar();
        if (ch=='\n')
        {
            str[i]='\0';
            break;
         }
         str[i++]=ch;
    }
    /* ইনপুট অংশ শেষ */

    /* সার্চ শুরু */
    for (i =1; ; i++)
    {
        if (str[i]==' '||ispunct(str[i])||str[i]=='\0') /* স্পেস, স্পেশাল ক্যারেকটার ও নাল টার্মিনেটর চেক */
        {
            if (isalnum(str[i-1])) /* আগের ক্যারেকটার টি এলফাবেট বা ডিজিট কি না সেটা চেক */
            word++;

            if (str[i]=='\0')
            break;
        }
    }
    printf("Number of words = %d", word);

    return 0;
}

দেখুন তো আপনারা চিন্তা করে অন্য একটি উপায় বের করতে পারেন কি না!!!

মৌলিক সংখ্যা : সংখ্যার মৌলিকতা পরীক্ষা

ড্রাফট পোস্টঃ


প্রাইম নাম্বার হল তারা যারা কেবল ১ এবং সেই নাম্বার দ্বারা নিঃশেষে বিভাজ্য। সহজ ও সুন্দর সংগা।



কাজেই আসো আমরা একটা নাম্বার প্রাইম কি না তা বের করার যৌক্তিক পদক্ষেপ গুলো নির্ধারণ করি।

ধারনা ১:

আমরা সংখ্যাটিকে প্রথমে ২ দিয়ে ভাগ করা শুরু করতে পারি। এরপর ৩, তারপর ৪, এভাবে সেই সংখ্যাটির আগ পর্যন্ত সকল সংখ্যা দ্বারা ভাগ করে দেখতে পারি যে কোন ভাগ শেষ আসে কি না। যেহেতু ১ দ্বারা সকল সংখ্যা বিভাজ্য এবং যেকোন সংখ্যাই সেই সংখ্যা দ্বারা বিভাজ্য তাই আমরা ২ থেকে শুরু করে সংখ্যাটির আগের সংখ্যা দ্বারা ভাগ করে দেখতে পারি।

কাজেই আমাদের ইনপুট নাম্বার যদি হয় n তাহলে আমরা 2 থেকে শুরু করে n-1 পর্যন্ত ভাগ করব। এই জন্য আমাদের for স্টেটমেন্ট হবে:

for (i =2; i <n;i++)
{
    flag=1;
    if(n%i==0)
    {
        flag=0;
        break;
    }
}

এরপরে আমরা খুব সহজেই flag এর ভ্যালু চেক করে বলতে পারব যে সংখ্যাটি প্রাইম কি না। কারণ আমরা প্রথমেই ধরে নিয়েছি যে flag এর ভ্যালু  ১ এবং সংখ্যাটি i এক কোন মান দ্বারা বিভাজ্য না হলে flag এর মান ১ ই থাকবে।

এই ধারনা দ্বারা ১ থেকে ৩৫০০০০ এর মধ্যেকার সকল প্রাইম নাম্বার (২৯৯৭৭ টি) বের করতে সময় লেগেছে ১১.৮৬ সেকেন্ড। (সময় নির্ধারণঃ ideone)

ধারণা ২:

আমরা ধারণা ১ কে একটু ঘষামাজা করি।
১০/১০=১, ১০/৫=২;
৮/৮= ১, ৮/৪=২;
..........................
n/n =1, n/(n/2)=2;

তাই কোন সংখ্যা তার অর্ধেকের বেশি মানের কোন সংখ্যা দ্বারা নিঃশেষে বিভাজ্য হতে পারে না।

তাই সেই সংখ্যার অর্ধেকের বেশি মানের কোন সংখ্যা দ্বারা তাকে ভাগ করা অর্থহীন।

কাজেই আমরা ২ দ্বারা ভাগ করা শুরু করব এবং সংখ্যাটির অর্ধেক মানের সংখ্যা দ্বারা ভাগ করেই থেমে যাবো। আমাদের for স্টেটমেন্ট হবে:

for(i =2; i <=n/2; i++)
{
    flag =1;
    if(n%i==0)
    {
        flag=0;
        break;
    }
}

ধারণা ২ তে আমাদের ভাগের কাজ হবে ধারণা ১ এর প্রায় অর্ধেক। এই যুক্তি ব্যবহার করে ১-৩৫০০০০ এর মধ্যেকার সব প্রাইম নাম্বার (২৯৯৭৭ টি) বের করতে সময় লেগেছে ৫.৯৪ সেকেন্ড। (সময় নির্ধারণঃ ideone)

ধারণা ৩:

আমরা ধারনা ২ কে আরো একটু স্লিম করার চেষ্টা করি। কেমন?

* সংগা অনুসারে ২ বাদে আর কোন জোড় প্রাইম নাম্বার থাকা সম্ভব না।
* একটি জোড় সংখ্যা সব সময়ই ২ দ্বারা বিভাজ্য
* একটি জোড় ও একটি বিজোড় সংখ্যার গুনফল সব সময়ই একটি জোড় সংখ্যা।

কাজেই কোন সংখ্যা যদি ২ দ্বারা বিভাজ্য না হয় তাহলে তাকে কোন জোড় সংখ্যা দ্বারা ভাগ করে দেখা অর্থহীন।

কাজেই, আমরা নাম্বারটিকে প্রথমে ২ দ্বারা ভাগ করে দেখব, এবং এর পরে যদি সংখ্যাটি ২ দ্বারা বিভাজ্য না হয় তাহলে শুধু বিজোড় সংখ্যা দ্বারা ভাগ করব। এবং অবশ্যই এই ভাগ চলবে সংখ্যাটির অর্ধেক মানে সংখ্যা পর্যন্ত।

কাজেই আমাদের লজিক হবে:


flag=1;
if (n==2)
    flag=1;
else if (n%2==0)
    flag=0;
else
    for (i = 3; i <=n/2; i += 2)
    {
        if(n%i==0)
        {
            flag=0;
            break;
        }
    }

ধারণা ৩ এ ভাগের কাজ হবে ধারণা ২ এর অর্ধেক। এই ধারনা ব্যবহার করে ১-৩৫০০০০ পর্যন্ত সব প্রাইম নাম্বার (২৯৯৭৭ টি) বের করতে সময় লেগেছে ২.৯৮ সেকেন্ড। (সময় নির্ধারণঃ ideone)

ধারণা ৪:

ইউক্লিড এর প্রস্তাবনা অনুসারে, যে কোন পূর্ণসংখ্যা হয় প্রাইম অথবা তার থেকে ছোট দুই বা ততোধিক প্রাইমের গুণফল। অর্থাৎ, যেকোন সংখ্যা তার চেয়ে ছোট একটি প্রাইম দ্বারা বিভাজ্য হবে যদি সেটি নিজে প্রাইম না হয়।

এখন একটা মজার জিনিস দেখি। ধরা যাক, ১০০ সংখ্যাটি প্রাইম কি না তা আমরা বের করবো। কাজেই ১০০ থেকে ছোট কোন প্রাইম দ্বারা যদি এটি বিভাজ্য না হয় তাহলেই এটি প্রাইম হবে। সবচে ছোট প্রাইম ২। ১০০/২=৫০। ৫০ এর কাছাকাছি প্রাইম হচ্ছে ৪৭। তাই ২ থেকে ৪৭ পর্যন্ত সংখ্যা দ্বারা যদি ১০০ বিভাজ্য না হয় তাহলেই ১০০ প্রাইম।

মজার জিনিসটা হচ্ছে, কোন সংখ্যা যদি প্রাইম না হয় তবে তা তার বর্গমূলের সমান বা তার চেয়ে ছোট কোন না কোন প্রাইম দ্বারা বিভাজ্য। আলোচ্চ্য ১০০ এর ক্ষেত্রে, ১০০ এর বর্গমূল ১০। তাই ২ থেকে ১০ এর মধ্যে কোন সংখ্যা দ্বারা যদি এটি বিভাজ্য না হয় তাহলেই এটি প্রাইম।

১০০ এর কাছাকাছি প্রাইমটি হচ্ছে ৯৭। ২, ৩, ৫, ৭  এই চারটি প্রাইমের কোনটি দ্বারাই ৯৭ বিভাজ্য নয়।

কাজেই,
আমরা যদি কোন সংখ্যার বর্গমূল বের করি এবং ২ থেকে শুরু করে সেই বর্গমূল পর্যন্ত সবগুলো সংখ্যা দিয়ে প্রদত্ত সংখ্যাটিকে ভাগ করে দেখি যে ভাগশেষ শূন্য নয়, তবেই সেটি প্রাইম নম্বর।

আমরা এখন ধারণা ৩ এর সাথে এই ধারণাটা যুক্ত করবো।

অর্থাৎ,


if (n==2)
    flag=1;
else if (n%2==0)
    flag=0;
else
{
    flag=1;
    m= (int) sqrt( (double) n);
    for (i = 3; i <=m; i+=2)
    {
        if (n%i==0)
        {
            flag=0;
            break;
        }
    }
}

এখন flag এর ভ্যালু চেক করলেই বোঝা যাবে যে n প্রাইম কি না।

এই ক্ষেত্রে সময় ধারনা ৩ এর চেয়েও কম লাগবে। এবং সেই কমটাও অনেক অনেক কম।

ধারণা ৪ ব্যবহার করে ১-৩৫০০০০ পর্যন্ত সবগুলো প্রাইম (২৯৯৭৭ টি) বের করতে সময় লেগেছে ০.০২ সেকেন্ড। (সময় নির্ধারণঃ ideone)

কারো ইচ্ছা থাকলে এই ধারণা ৪ টি ব্যবহার করে ১০০০০০ পর্যন্ত যতগুলো প্রাইম নম্বর আছে তা বের করে দেখতে পারো যে কোন ধারণায় কতটা সময় লাগে আউটপুট আসতে।

C কিওয়র্ড সমূহ : C Keywords

প্রোগ্রামিং ল্যাংগুয়েজ এর ক্ষেত্রে কিওয়র্ড হচ্ছে এমন কিছু word বা শব্দ যাদের অর্থ ঐ প্রোগ্রামিং ল্যাংগুয়েজ এ নির্দিষ্ট। এরা নির্দিষ্ট কিছু কাজ সম্পাদন করে থাকে যা ঐ ভাষার স্ট্যান্ডার্ড নিয়ম অনুযায়ী পূর্ব নির্ধারিত। ঐ সব কাজ ছাড়া এদেরকে আর অন্য কোন স্থানে ব্যবহার করা যায় না। কোন ইডেন্টিফায়ার (ভ্যারিয়েবল, ফাংশন ইত্যাদির নাম) হিসেবে এদেরকে ব্যবহার করা যায় না।

মোট কথা, কিওয়র্ড হল কোন ভাষার নির্দিষ্ট কিছু শব্দ যাদের কাজ ঐ ভাষার ব্যাকরণ অনুযায়ী পূর্ব নির্ধারিত এবং যারা অপরিবর্তনীয়।



ANSI স্ট্যান্ডার্ড অনুযায়ী C ভাষায় ৩২ টি কিওয়র্ড আছে। এই ৩২ টি কিওয়র্ড ও এদের ব্যবহারের ব্যাকরণ নিয়ে ANSI C ভাষাটি গঠিত। ৩২ টি কিওয়র্ড হলঃ

auto, break, case, char, const, continue, default, do, double, else, enum, extern, float, for, goto, if, int, long, register, return, short, signed, sizeof, static, struct, switch, typedef, union, unsigned, void, volatile, while

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

  • প্রোগ্রামের প্রবাহ নিয়ন্ত্রক (flow control): এই কিওয়র্ডগুলো কোন প্রোগ্রামে কোড এক্সেকিউশনের ধারা ও ধারাবাহিকতা নিয়ন্ত্রন করে। এদেরকে আবার তিনটি উপশ্রেণী তে বিভক্ত করা যায়ঃ


            1. ব্রাঞ্চিং কিওয়র্ডঃ এরা হচ্ছে C এর মাল্টিপল সিলেকশন বা বহুমুখী পথ নির্বাচন সম্পর্কিত কিওয়র্ড। এরা নির্দিষ্ট শর্তের উপর নির্ভর করে কোন কোড এক্সেকিউট হবে, আর কোন কোড এক্সেকিউট হবে না, সেটা নির্ধারণ করে। এরা হলঃ if, else, switch, case, break, default

              2. লুপিং কিওয়র্ডঃ এরা হচ্ছে C এর পুনরাবৃত্তকরন বা reparation কিওয়র্ড। এরা C এর বিভিন্ন কোড সেগমেন্টকে নির্দিষ্ট শর্ত সাপেক্ষে বারবার এক্সেকিউট করতে সাহায্য করে। অর্থাৎ, এই কোডগুলো লুপ তৈরীতে সহায়তা করে থাকে। এরা হলঃ for, while, do, break, continue

              3. জাম্প কিওয়র্ডঃ এরা প্রোগ্রামের এক স্থান থেকে অন্য স্থানে প্রোগ্রাম এক্সেকিউশনের তাৎক্ষণিক স্থানান্তর ঘটায়। এই জন্যেই এদেরকে জাম্প কিওয়র্ড বলা হচ্ছে। এরা হলঃ return, goto


  • ডেটা টাইপ সম্পর্কিতঃ C ডেটা টাইপ সমূহ প্রয়োগ ও তাদের রূপান্তরকরণ সম্পর্কিত কাজ এই কিওয়র্ডগুলোর সহায়তায় সম্পন্ন হয়। এদেরকে ৬ টি উপশ্রেণী তে বিভক্ত করা হয়েছেঃ


              1. মৌলিক ডেটা টাইপ সমূহঃ C এর পূর্ব নির্ধারিত ৫ টি ডেটা টাইপ ব্যবহারের জন্য এই ৫ টি কিওয়র্ড ব্যবহার করা হয়। এরা হলঃ

char, int, float, double, void

              2. টাইপ লিমিট মডিফায়ার সমূহঃ এরা মৌলিক ডেটা টাইপ সমূহের ধারণক্ষমতায় কিছু পরিবর্তন আনে। এরা হচ্ছে,

short, long, signed, unsigned

              3. স্টোরেজ ক্লাস স্পেসিফায়ারঃ এরা বিভিন্ন ডেটা টাইপের ডেটা সম্বলিত ভ্যারিয়েবল সমূহ মেশিনের কোন ধরনের মেমরিতে জমা থাকবে সেই বিষয়ক নির্দেশনা দান করে। এদের ব্যবহার নিত্যনৈমিত্তিক না হলেও, দ্রুতগতির ও স্বল্প মেমরী ব্যবহারকারী প্রোগ্রাম তৈরীতে এদের ব্যবহার অপরিহার্য। এরা হলঃ

auto, extern, register, static

              4. এক্সেস মডিফায়ারঃ কোন ডেটার পরিবর্তন কিভাবে হবে বা আদৌ হবে কি না সেই সম্পর্কিত নির্দেশনা এরা দিয়ে থাকে। এদের ব্যবহারও মূলত অভিজ্ঞ প্রোগ্রামারদের হাতেই হয়ে থাকে। এরা হলঃ

const, volatile

              5. স্ট্রাকচার সম্পর্কিতঃ C ডেটা স্ট্রাকচার সম্পর্কিত কিওয়র্ড। এরা হলঃ

struct, union

              6. ইউজার ডিফাইন্ড ডেটা টাইপ সম্পর্কিতঃ এরা বিভিন্ন ইউজার ডিফাইন্ড ও এডভান্সড ডেটা টাইপ তৈরিকরণে সাহায্য করে। এরা হলঃ

typedef, enum


  • অপারেটঃ একটি মাত্র অপারেটর ই কিওয়র্ড হিসেবে বিদ্যমান। এটি কোন ডেটা টাইপ বা ভ্যারিয়েবল (যে কোন ধরণের) এর আকার বাইট (Byte) এককে হিসাব করে ব্যবহারকারী কে জানায়। এটি হলঃ


sizeof

সবগুলো কিওয়র্ডের আলাদা আলাদা সংক্ষিপ্ত কাজ ও ব্যবহার এর জন্য এখানে দেখতে পারেন।

তবে এদের পূর্ণাঙ্গ ব্যবহার কেবলমাত্র এদের নিয়মিত প্রয়োগের মাধ্যমেই শেখা সম্ভব, মুখস্থ করে নয়।

C এক্সপ্রেশন : Expression

এক্সপ্রেশন শব্দের অর্থ অভিব্যক্তি। এমন কিছু আচার আচরণ, ইশারা ইঙ্গিত, কথাবার্তা, শব্দ, মুখভঙ্গি যা আমাদের মনের একটি নির্দিষ্ট ভাব প্রকাশ করে তাই হচ্ছে অভিব্যক্তি। অভিব্যক্তি দ্বারা ভাব প্রকাশ করা হয়, অভিব্যক্তিকে সব সময়ই অর্থবহ হতে হয়।

C এক্সপ্রেশন সমূহও অনেকটা তেমনই। C ভাষায় এক্সপ্রেশন হচ্ছে অপারেটর ও অপার‍্যান্ড এর সমন্বয়ে তৈরী কোড সেগমেন্ট যার একটি নির্দিষ্ট চুড়ান্ত মান আছে। অর্থাৎ ঐ কোড সেগমেন্টকে এক্সেকিউট করলে, অপারেটর দের কাজ শেষ হবার পরে ১ টি মাত্র চুড়ান্ত মান পাওয়া যাবে। এই চুড়ান্ত মান গাণিতিক বা সংখ্যা মান হতে পারে, আবার যৌক্তিক বা লজিকার মান (সত্য বা মিথ্যা; True বা False) ও হতে পারে।

মোট কথা হল, এক্সপ্রেশন হচ্ছে এমন একটি কোড সমন্বয় যার একটি মান আছে। যেমন, ধরা যাক a, b ও c তিনটি ইন্টিজার ভ্যারিয়েবল। নিচের এসাইনমেন্ট স্টেটমেন্টটি খেয়াল করা যাক,

a= b+c;

এখানে এসাইনমেন্ট অপারেটর এর বাম পাশে আছে a এবং ডান পাশে আছে কোড সেগমেন্ট b+c। b+c এর অর্থ হচ্ছে b ও c তে জমা থাকা ভ্যালুর যোগফল। অর্থাৎ,  b+c এর একটি চূড়ান্ত মান আছে। তাই b+c অংশটি হচ্ছে একটি এক্সপ্রেশন।

এক্সপ্রেশন প্রায় সব সময়ই অপারেটর ও অপার‍্যান্ড (ভ্যারিয়েবল, কনস্ট্যান্ট ইত্যাদি) এর সমন্বয়ে গঠিত। এবং এক্সপ্রেশন এর সব সময়ই একটি লজিকাল বা যৌক্তিক মান থাকে। চুড়ান্ত মানের উপর ভিত্তি করে তার যৌক্তিক মান হয় সত্য অথবা মিথ্যা হয়।

C তে কোন বুলিয়ান (Boolean) True বা False নেই। কিন্তু অন্য একটি উপায়ে C তে সত্য বা মিথ্যা নিরূপন করা যায়। যখন কোন এক্সপ্রেশনের চুড়ান্ত সংখ্যা মান শূন্য (0) তখন সেই এক্সপ্রেশনটি মিথ্যা, অর্থাৎ লজিকাল মান হল False। যে কোন অশূন্য সংখ্যা মানের জন্য কোন এক্সপ্রেশন সত্য, অর্থাৎ লজিকালি True।
উল্টোভাবে বলতে গেলে, C ভাষায় সকল মিথ্যা এক্সপ্রেশন এর সংখ্যা মান 0 আর সত্য এক্সপ্রেশন এর মান অশূন্য (সাধারণত 1)।

মনে রাখতে হবে যে, অশূন্য মানের সকল এক্সপ্রেশন ই সত্য, তবে সাধারণত সত্য এক্সপ্রেশন এর মানকে  1 দ্বারা প্রকাশ করা হয়। এর মানে এই নয় যে, কেবল 1 ই সত্য বা True। বস্তুত কোন এক্সপ্রেশন এর মান শূন্য না হলেই তা সত্য।

এবার বলেন দেখি নিচের কোডের আউটপুট কত?

#include<stdio.h>

int main()
{
    int a, b, c, d;
    c=1;
    d=10;

    a= c>d;
    b= d<c;

    printf("%d %d", a, b);

return 0;
}

অপারেটর ও অপার‍্যান্ড এর উপর ভিত্তি করে এক্সপ্রেশন অনেক ধরনের হতে পারে। যেমন,

গাণিতিক বা Arithmetic অপারেটর কোন এক্সপ্রেশন এ থাকলে তাকে গাণিতিক বা arithmetic এক্সপ্রেশন বলে। আবার কোন গাণিতিক এক্সপ্রেশনে উপস্থিত অপার‍্যান্ড এর ধরন এর উপর ভিত্তি করে,
ইন্টিজার এক্সপ্রেশন,
ফ্লোটিং পয়েন্ট বা রিয়েল এক্সপ্রেশন
এবং মিশ্র বা মিক্সড এক্সপ্রেশন এই তিন ভাগে ভাগ করা যায়।

লজিকাল অপারেটর এর এক্সপ্রেশন হচ্ছে লজিকাল এক্সপ্রেশন আবার, কন্ডিশনাল অপারেটর সম্বলিত এক্সপ্রেশনকে কন্ডিশনাল এক্সপ্রেশন বলে।

এরকম আরো অনেক ধরনের এক্সপ্রেশন আছে।

C ভাষায় এক্সপ্রেশন খুব গুরুত্বপূর্ণ ভূমিকা পালন করে। কারণ C ভাষায় অনেক ধরনের অপারেটর আছে এবং বেশির ভাগ নির্দেশনাই এক্সপ্রেশন আকারে প্রকাশ করা হয়।