أستمع الى المقال

الخوارزميات Algorithms:

استعرضنا الخوارزميات بطريقة عامة في الدرس الثاني من دورة تعلم كوتلن، ولكن في هذه المقالة سنتحدث عنها بتفصيل أكبر. ومن ثم في نهاية الدورة بعد فهم أساسيات لغة البرمجة، سنتعلم المزيد عن الخوارزميات عبر الأمثلة العملية.

يتمثل أحد أهم الاختلافات بين خوارزميات الحياة الواقعية – مثل خوارزمية طهي المعكرونة التي تكلمنا عنها في الدرس السابق ذكره – وخوارزميات الحاسب، في أن الحاسب لا يمكنه تخمين ما نريد القيام به. إذا حدث خطأ ما أو كانت الخوارزمية غير واضحة، فيمكن للإنسان تعديل الخوارزمية بناءً على تجربته. لا تستطيع أجهزة الحاسب أن تفعل الشيء نفسه، إذ يجب وصف خوارزمية الحاسب بدقة وبشكل لا لبس فيه.

يعتبر كود التطبيق أو البرنامج عبارة عن سلسلة من التعليمات لأداء بعض المهام على جهاز الحاسب. ويكون الاختلاف بين البرامج والخوارزميات، في أن البرامج تتم كتابتها باستخدام لغة برمجة معينة. بينما يتم وصف الخوارزميات عادةً على مستوى لغوي أعلى من رموز وعبارات لغات البرمجة.

بكلمات أخرى، تشبه الخوارزمية مخططًا تجريديًا، ويمكن أن يكون البرنامج هو تنفيذ لهذا المخطط. وهذا يعني أيضًا أن الخوارزميات حيادية اللغة: أي يمكن تنفيذ خوارزمية واحدة باستخدام لغات برمجة مختلفة. مثلا، يمكننا استخدام Java أو Python أو Kotlin أو لغات أخرى لتنفيذ نفس الخوارزمية.

تحتوي لغات البرمجة عادةً على تطبيقات لبعض الخوارزميات القياسية التي توفر حل للمشكلات المعروفة مسبقًا. يتم توفير هذه الخوارزميات في مكتبات مرفقة مع لغة البرمجة. ويمكن للمبرمج الذي يستخدم لغة برمجة ما، إعادة استخدام هذه الخوارزميات المتوفرة مع هذه اللغة، عوضا عن كتابة واحدة جديدة في كل مرة.

ومع ذلك، ليكون هذا المبرمج قادرًا على استخدام هذه الخوارزميات القياسية بشكل صحيح وفعال وفهم كيفية استخدام المطورين الآخرين لها، من الضروري تعلمها والتعرف على كيفية عملها. بالإضافة إلى أنه، لا يمكن أن تغطي الخوارزميات من المكتبات القياسية المرفقة مع لغات البرمجة، جميع المشكلات المحتملة التي قد يواجهها المبرمج. لذلك في بعض الأحيان، سيتعين عليك عادةً إنشاء خوارزميات خاصة بك وتنفيذها لحل مشاكلك. وبالطبع، سيحسن تعلم وفهم الخوارزميات، مهارات البرمجة لديك إلى حد بعيد.

تعقيد الوقت Time Complexity:

هو قياس الوقت الذي تقضيه الخوارزمية في معالجة البيانات. إذا افترضنا أن لدينا خوارزميتين كتبتا لحل نفس المشكلة، كيف يمكن اختيار الأفضل بينهما؟ لمعرفة الأفضل بين الخوارزمتين، نحتاج لقياس الوقت التي تستغرقه كل منهما لمعالجة البيانات.

إحدى الطرق يمكننا كتابة الكود الذي يمثل الخوارزمية، وتنفيذ الكود كبرنامج عادي، ومن ثم قياس الوقت الذي يقضيه كل برنامج في معالجة البيانات. ولكن في هذه الحالة سنواجه مشكلة اختلاف أجهزة الحاسب. فالوقت الذي يستغرقه عمل الخوارزمية في حاسب ضعيف المواصفات ليس هو نفس الوقت في حاسب مواصفاته أقوى. بالإضافة إلى حجم البيانات التي تعالجها الخوارزمية، فكل جهاز حاسب يختلف عن الآخر في معالجة البيانات بنفس الحجم.

لذا، توجب إيجاد طرق أخرى لقياس كفاءة الخوارزمية بعيدًا عن أجهزة الحاسب، إحدى الطرق وضع الخوارزمية في تصنيف من الأفضل إلى الأسوأ، عبر استخدام الـ Big-O Notation.

تصنيف الـ Big-O Notation:

يعتمد وقت تشغيل الخوارزمية على البيانات المدخلة وعدد العمليات التي ستقوم بها الخوارزمية حتى نهاية عملها. فمثلًا، إذا كان لدينا ملف به مليون 1000000 رقم، وكان لدينا خوارزمية للبحث عن الأعداد الأولية Prime Numbers في قائمة الأرقام هذه، ونريد حساب كفاءة هذه الخوارزمية، فإننا سنرمز لعدد العمليات التي ستجري بالحرف (n). وبحساب عدد العمليات التي يمكن القيام بها حتى الوصول للنتيجة النهائية، يمكننا حساب كفاءة الخوارزمية بعيدًا عن أجهزة الحاسب.

الـ Big-O Notation له ميزة أساسية واحدة، فهو يصف أسوأ أداء للخوارزمية. لذلك يمكننا حساب مقدار الوقت المخصص لمعالجة كمية معينة من بيانات الإدخال والتأكد من أن الخوارزمية ستعالج كل ذلك في أقل وقت ممكن. عمليا، حين تنفيذ هذه الخوارزمية كبرنامج يعمل في حاسب ما، قد تعمل الخوارزمية في بعض الأحيان بشكل أفضل من تصنيف الـ Big-O Notation الذي تم تصنيفها به، ولكن ليس أسوأ.

بعض تقييمات مقدار زيادة الوقت الشائعة عبر الـ Big-O Notation:

المعادلات التالية تستخدم في حساب الوقت الذي تستغرقه الخوارزمية من الأفضل إلى الأسوأ:

  1. معادلة (constant time – وقت ثابت) – (1)O.
  2. معادلة (logarithmic time – وقت لوغاريثمي) – (log n)O.
  3. معادلة (square root time – وقت الجذر التربيعي) – (n)O.
  4. معادلة (linear time – وقت خطي) (n)O.
  5. معادلة (log-linear time – وقت خطي لوغاريثمي) (n log n)O.
  6. معادلة (quadratic time – وقت تربيعي) (n2)O.
  7. معادلة (exponential time – وقت أسي) (2n)O.
  8. معادلة (factorial time – وقت عاملي) (!n)).

تنويه: كان هذا عرض نظري عن الخوارزميات. وبالطبع سنعود إليها في دروس عملية قادمة بعد دراسة أساسيات البرمجة عبر كوتلن لأن الشرح العملي للخوارزميات، يعتمد بشكل كبير على فهم هذه الأساسيات.

هياكل البيانات Data structures:

لا يمكن تجاهل الخوارزميات إذا كنا نأمل في كتابة كود برنامج مبتكر وذا أداء عالي. ومع ذلك، فإن الخوارزميات ليست الشيء الوحيد الذي نحتاجه: إلى جانب معالجة البيانات والتي تهتم بها الخوارزميات، هناك أيضًا مسألة تخزين البيانات، بما في ذلك مقدار المساحة التي تحتاجها. هنا يأتي دور هياكل البيانات، لذلك سنستعرض هنا أساسيات هياكل البيانات.

ما هي هياكل البيانات:

هياكل البيانات هي طريقة لتنظيم البيانات وتوفير وصول ملائم إليها. وكما يقال، بالمثال يتضح المقال، إليكم المثال التالي:

الخوارزميات وهياكل البيانات
مصدر الصورة أكاديمية JetBrains

فلنتخيل أن لدينا عدداً كبيراً من زجاجات المشروبات والتي نرغب في تنظيمها وترتيبها على نحو ما. يمكننا وضعها جميعها في كيس بشكل عشوائي، أو وضعها فوق بعضها في شكل برج. ولكن في أي من الطريقتين، سيصعب الوصول إلى زجاجة معينة منهم، أو حتى حساب عددها. ولكن ماذا إذا رتبنا هذه الزجاجات في آلة بيع المشروبات؟. في هذه الحالة ستكون مرتبة في رفوف وصفوف، وسيسهل الوصول إلى أي واحدة منها، بالإضافة إلى أننا يمكننا عدها وحساب ما نقص منها.

دعونا الآن نعود إلى التعريف السابق ونحاول فهم هياكل البيانات مجددا: يشير مصطلح بنية أو هياكل البيانات إلى تجميعات Collections من العناصر elements والتي تحتوي على البيانات، وكذلك العلاقات فيما بينها، والعمليات التي تجري على البيانات.

كقاعدة عامة، تتكون العمليات التي تجري على البيانات، من:

  • عمليات داخلية: وهي العمليات التي تدعم تنظيم البيانات.
  • عمليات خارجية: وهي العمليات التي يقوم بها المستخدم user في تفاعله مع البيانات، مثل عمليات: التخزين، الحذف، التعديل، واسترجاع وعرض البيانات المخزنة.

هناك العديد من هياكل البيانات الشائعة، مثل: array, linked list, hash table, etc، وأيضًا هناك العديد من الهياكل الهرمية في شكل شجرة، مثل:

binary search tree, heap, red-black tree, B-tree, etc. سنشرح كل واحدة منها بشكل تفصيلي في مقالات لاحقة. أما هذه المقالة فهي عرض أساسي ومدخل لعالم هياكل البيانات.

نوع البيانات المجردة Abstract data type:

هناك مصطلح آخر: نوع البيانات المجردة (ADT)، والذي يستخدم أحيانًا كمرادف لهياكل البيانات، على الرغم من أن هذا ليس صحيحًا تمامًا. فـ ADT هي ببساطة طريقة لعرض هياكل البيانات من مستوى أعلى من مستوى تعليمات الآلة، ورؤية وظائفها وسلوكها العام. مثل أن يتفاعل المستخدم مع آلة بيع المشروبات بوضع النقود واختيار المشروب وانتظار خروج المشروب، غض النظر عما يجري في داخل الآلة في المستوى الأسفل، لا يهم المستخدم هذا.

قائمة الانتظار queue، هي أحد أنواع هياكل البيانات. حيث يكون العنصر الأول الذي يدخل إلى القائمة هو أول عنصر يترك القائمة عند استخراج عنصر ما (يدخل أولًا، يخرج أولًا First in first out – FIFO). كمثال، تخيل أنك تقف في صف في سوبر ماركت وتلتزم الآداب العامة للاصطفاف وتنتظر دورك وتلتزم بقانون: من يأتي أولاً يذهب أولاً. حسنًا، البيانات لا تعرف هذه القوانين لأنها غبية وهي مجرد أرقام بدون تعليمات. وكما نفعل إذا أتى شخص وحاول القفز إلى مكان أمامي في الصف، سنخبره بكل بساطة، “الرجاء التزام الصف”. ولجعل البيانات تلتزم القوانين، علينا أن نوجه لها تعليمات بذلك. وهنا يأتي دور هياكل البيانات، فهي توفر الإرشادات للبيانات لكي تتصرف مثل ADT!. هياكل البيانات هي تنفيذ لوظائف الـ ADT، في المستوى الأدنى من الآلة.

كما قلنا من قبل، نحن كبشر نعرف كيفية الوقوف في صف الانتظار ولكن لكي تفهم البيانات كيفية الالتزام في قائمة الانتظار، يجب أن تخبر الحاسب بكيفية التعامل مع البيانات ليجعلها الحاسب تنتظم في صفوف. علينا إخبار البيانات بعدم القفز والتصرف وفقًا للوظائف الأساسية لـ ADT. يمكننا القيام بذلك من خلال الخوارزميات وكتابة وظائف Functions تخبر البيانات كيف تتصرف أو تنظم نفسها في الذاكرة.

هل أعجبك المحتوى وتريد المزيد منه يصل إلى صندوق بريدك الإلكتروني بشكلٍ دوري؟
انضم إلى قائمة من يقدّرون محتوى إكسڤار واشترك بنشرتنا البريدية.