تمارين برمجية - كتاب الذكاء الإصطناعي - الصف 12 - الفصل 1 - المملكة العربية السعودية

الكتاب: كتاب الذكاء الإصطناعي - الصف 12 - الفصل 1 | المادة: الذكاء الإصطناعي | المرحلة: الصف 12 | الفصل الدراسي: 1

الدولة: المملكة العربية السعودية | المنهج: المنهج السعودي - وزارة التعليم

الدرس: تمارين برمجية

📚 معلومات الصفحة

الكتاب: كتاب الذكاء الإصطناعي - الصف 12 - الفصل 1 | المادة: الذكاء الإصطناعي | المرحلة: الصف 12 | الفصل الدراسي: 1

الدولة: المملكة العربية السعودية | المنهج: المنهج السعودي - وزارة التعليم

نوع المحتوى: تمارين وأسئلة

مستوى الصعوبة: متوسط

📝 ملخص الصفحة

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

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

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

📄 النص الكامل للصفحة

--- SECTION: 4 --- وضح مزايا استخدام الاستدعاء الذاتي وعيوبه.--- SECTION: 5 --- اكتب دالة استدعاء تكرارية بلغة البايثون تقوم بحساب الرقم الأكبر بترتيب محدد (مثلاً ثاني أكبر رقم) في قائمة من الأرقام.--- SECTION: 6 --- اكتب دالة استدعاء تكرارية بلغة البايثون لحساب مجموع كل الأرقام الزوجية في قائمة معينة.--- VISUAL CONTEXT ---Table Structure: Headers: N/A Data: لا توجد بيانات رقمية أو رسوم بيانية في هذا العنصر البصري.Context: يشير إلى الجهة الناشرة أو المصدر الرسمي للمحتوى التعليمي.

✅ حلول أسئلة الكتاب الرسمية

عدد الأسئلة: 3

سؤال 4: وضّح مزايا استخدام الاستدعاء الذاتي وعيوبه.

الإجابة: س4: المزايا: تبسيط الحل، مناسب للهرمية. العيوب: استهلاك الذاكرة، أبطأ، خطر التكرار.

خطوات الحل:

  1. **الشرح:** الاستدعاء الذاتي (أو العودية) هو أسلوب برمجي حيث تستدعي الدالة نفسها لحل مشكلة أصغر من نفس النوع. لنفهم مزايا وعيوب هذا الأسلوب. **المزايا:** 1. **تبسيط الحل:** يجعل الكود أنظف وأسهل للقراءة والفهم، خاصة للمشكلات التي يمكن تقسيمها إلى مشكلات فرعية متشابهة (مثل عمليات البحث في الأشجار أو حساب المضروب). 2. **مناسب للهرمية:** مثالي لمعالجة الهياكل الهرمية أو المتداخلة بشكل طبيعي، مثل الملفات في المجلدات أو العقد في الشجرة. **العيوب:** 1. **استهلاك الذاكرة:** كل استدعاء يستهلك مساحة في مكدس الاستدعاء، مما قد يؤدي إلى نفاد الذاكرة إذا كان العمق كبيراً (مشكلة تجاوز المكدس). 2. **أبطأ:** عادةً ما يكون أبطأ من الحلول التكرارية بسبب تكلفة استدعاء الدالة مراراً. 3. **خطر التكرار:** في بعض الحالات (مثل حساب متتالية فيبوناتشي بشكل ساذج)، قد تحسب نفس القيم مرات عديدة، مما يهدر الوقت. إذن، الإجابة هي: **المزايا: تبسيط الحل، مناسب للهياكل الهرمية. العيوب: استهلاك الذاكرة، أبطأ، خطر التكرار.**

سؤال 5: اكتب دالة استدعاء تكرارية بلغة البايثون تقوم بحساب الرقم الأكبر بترتيب محدد (مثلاً ثاني أكبر رقم) في قائمة من الأرقام.

الإجابة: س5: فكرة الحل لإيجاد العدد ذو الرتبة k: 1. دالة لإيجاد الأكبر: حالة التوقف (عنصر واحد)، التكرار (مقارنة الأول مع أكبر البقية). 2. دالة للرتبة k: إذا 1 = k نرجع الأكبر. وإلا نحذف الأكبر ونستدعي الدالة لـ k-1.

خطوات الحل:

  1. **الخطوة 1 (المعطيات والفهم):** لدينا قائمة من الأرقام وترتيب محدد (مثلاً k=2 للثاني أكبر). نريد كتابة دالة استدعاء ذاتي (تكرارية) في بايثون لإيجاد العنصر ذو الرتبة k (حيث الرتبة 1 هي الأكبر، 2 هو الثاني أكبر، وهكذا). الفكرة هي إيجاد الأكبر، ثم إزالته، والبحث عن الرتبة المطلوبة في البقية.
  2. **الخطوة 2 (خطوات الخوارزمية):** 1. **دالة مساعدة لإيجاد الأكبر:** - حالة التوقف: إذا كانت القائمة تحتوي على عنصر واحد، فهو الأكبر. - الخطوة التكرارية: نقارن العنصر الأول بأكبر عنصر في بقية القائمة (باستدعاء ذاتي)، ونرجع الأكبر منهما. 2. **الدالة الرئيسية للرتبة k:** - حالة التوقف: إذا كان k=1، نرجع أكبر عنصر في القائمة (باستخدام الدالة المساعدة). - الخطوة التكرارية: نجد أكبر عنصر، نحذفه من القائمة، ثم نستدعي الدالة الرئيسية على القائمة الجديدة مع الرتبة k-1.
  3. **الخطوة 3 (كتابة الكود):** python def find_kth_largest(lst, k): # دالة مساعدة لإيجاد الأكبر def find_max(sublist): if len(sublist) == 1: return sublist[0] max_of_rest = find_max(sublist[1:]) return sublist[0] if sublist[0] > max_of_rest else max_of_rest # حالة التوقف للدالة الرئيسية if k == 1: return find_max(lst) # إيجاد الأكبر وحذفه max_val = find_max(lst) new_lst = [x for x in lst if x != max_val] # تحذف أول ظهور للأكبر # أو نستخدم remove() لكن بحذر لتجنب الأخطاء إذا تكرر الرقم # استدعاء ذاتي للرتبة k-1 return find_kth_largest(new_lst, k-1) # مثال: # print(find_kth_largest([3, 1, 4, 1, 5], 2)) # يجب أن يطبع 4 (الثاني أكبر) **الخطوة 4 (النتيجة):** إذن، الدالة تكرارية تقوم بإيجاد العنصر ذو الرتبة k في قائمة، باستخدام فكرة إيجاد الأكبر وحذفه بشكل متكرر. **ملاحظة:** هذا الحل بسيط للشرح، ولكن قد يكون غير فعال للقوائم الكبيرة بسبب التعقيد الزمني العالي.

سؤال 6: اكتب دالة استدعاء تكرارية بلغة البايثون لحساب مجموع كل الأرقام الزوجية في قائمة معينة.

الإجابة: س6: مجموع الأعداد الزوجية: - حالة التوقف: القائمة فارغة = 0. - الخطوة التكرارية: إذا كان العنصر الأول زوجياً نضيفه لمجموع البقية، وإلا نحسب مجموع البقية فقط.

خطوات الحل:

  1. **الخطوة 1 (المعطيات والفهم):** لدينا قائمة من الأرقام. نريد كتابة دالة استدعاء ذاتي (تكرارية) في بايثون لحساب مجموع كل الأرقام الزوجية فيها. الرقم الزوجي هو الذي يقبل القسمة على 2 بدون باقي (أي باقي القسمة يساوي 0).
  2. **الخطوة 2 (خطوات الخوارزمية):** 1. **حالة التوقف:** إذا كانت القائمة فارغة، فالمجموع هو 0. 2. **الخطوة التكرارية:** - نفحص العنصر الأول في القائمة. - إذا كان زوجياً (أي `العنصر الأول % 2 == 0`)، نضيف قيمته إلى مجموع الأرقام الزوجية في بقية القائمة (التي نحسبها باستدعاء ذاتي). - إذا كان فردياً، نحسب فقط مجموع الأرقام الزوجية في بقية القائمة (باستدعاء ذاتي).
  3. **الخطوة 3 (كتابة الكود):** python def sum_even_recursive(lst): # حالة التوقف: القائمة فارغة if not lst: return 0 # نفحص العنصر الأول first_element = lst[0] rest_of_list = lst[1:] # بقية القائمة # إذا كان العنصر الأول زوجياً if first_element % 2 == 0: return first_element + sum_even_recursive(rest_of_list) else: # إذا كان فردياً return sum_even_recursive(rest_of_list) # مثال: # print(sum_even_recursive([1, 2, 3, 4, 5])) # يجب أن يطبع 6 (لأن 2+4=6) **الخطوة 4 (النتيجة):** إذن، الدالة تكرارية تقوم بحساب مجموع الأرقام الزوجية في قائمة، باستخدام فكرة فحص العنصر الأول وإضافة قيمته إذا كان زوجياً، ثم متابعة الحساب على بقية القائمة بشكل متكرر حتى تصبح فارغة.

📝 أسئلة اختبارية

عدد الأسئلة: 3

سؤال 4: وضح مزايا استخدام الاستدعاء الذاتي وعيوبه.

  • أ) المزايا: كفاءة عالية، العيوب: تعقيد الكود
  • ب) المزايا: توفير الذاكرة، العيوب: بطء التنفيذ
  • ج) المزايا: بساطة الكود، العيوب: استهلاك ذاكرة أكثر
  • د) المزايا: سرعة التنفيذ، العيوب: صعوبة الفهم

الإجابة الصحيحة: مزايا الاستدعاء الذاتي: 1. بساطة ووضوح الكود 2. مناسب للمشاكل التي يمكن تقسيمها إلى مشاكل فرعية متشابهة 3. يقلل من الحاجة إلى حلقات التكرار. عيوبه: 1. قد يكون أبطأ في التنفيذ 2. يستهلك ذاكرة أكثر بسبب تكدس استدعاءات الدوال 3. قد يسبب تجاوز سعة المكدس (stack overflow) إذا كان التكرار عميقاً جداً.

الشرح: الاستدعاء الذاتي مفيد للخوارزميات المتكررة مثل الفرز والبحث، لكنه قد يكون غير فعال للعمليات البسيطة التي يمكن حلها بالتكرار العادي.

تلميح: فكر في كيفية عمل الاستدعاء الذاتي في الذاكرة وكيفية تقسيم المشاكل.

سؤال 5: اكتب دالة استدعاء تكرارية بلغة البايثون تقوم بحساب الرقم الأكبر بترتيب محدد (مثلاً ثاني أكبر رقم) في قائمة من الأرقام.

  • أ) def largest_nth(lst, n): return sorted(lst)[-n]
  • ب) def find_nth_largest(lst, n): if len(lst) == 0: return None max_val = max(lst) if n == 1: return max_val new_lst = [x for x in lst if x != max_val] return find_nth_largest(new_lst, n-1)
  • ج) def nth_largest(lst, n): for i in range(n): m = max(lst) lst.remove(m) return m
  • د) def recursive_nth(lst, n): lst.sort() return lst[-n]

الإجابة الصحيحة: def find_nth_largest(lst, n): if len(lst) == 0: return None max_val = max(lst) if n == 1: return max_val new_lst = [x for x in lst if x != max_val] return find_nth_largest(new_lst, n-1)

الشرح: تستخدم الدالة الاستدعاء الذاتي لإيجاد أكبر رقم ثم إزالته من القائمة وتكرار العملية حتى الوصول للترتيب المطلوب.

تلميح: يمكنك استخدام الدالة max() لإيجاد أكبر قيمة ثم إنشاء قائمة جديدة بدونها.

سؤال 6: اكتب دالة استدعاء تكرارية بلغة البايثون لحساب مجموع كل الأرقام الزوجية في قائمة معينة.

  • أ) def sum_even(lst): total = 0 for num in lst: if num % 2 == 0: total += num return total
  • ب) def sum_even_numbers(lst): if len(lst) == 0: return 0 first = lst[0] rest = lst[1:] if first % 2 == 0: return first + sum_even_numbers(rest) else: return sum_even_numbers(rest)
  • ج) def recursive_even_sum(lst): return sum([x for x in lst if x % 2 == 0])
  • د) def even_sum(lst): if not lst: return 0 return (lst[0] if lst[0] % 2 == 0 else 0) + even_sum(lst[1:])

الإجابة الصحيحة: def sum_even_numbers(lst): if len(lst) == 0: return 0 first = lst[0] rest = lst[1:] if first % 2 == 0: return first + sum_even_numbers(rest) else: return sum_even_numbers(rest)

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

تلميح: تذكر أن الشرط الأساسي هو عندما تكون القائمة فارغة، وعندها ترجع 0.

🎴 بطاقات تعليمية للمراجعة

عدد البطاقات: 4 بطاقة لهذه الصفحة

ما هي المزايا الرئيسية لاستخدام الاستدعاء الذاتي (Recursion) في البرمجة؟

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

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

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

ما هي أبرز عيوب استخدام الاستدعاء الذاتي (Recursion) في البرمجة؟

الإجابة: تشمل العيوب الرئيسية للاستدعاء الذاتي استهلاك كمية كبيرة من ذاكرة المكدس (Stack Memory) مما قد يؤدي إلى تجاوز سعة المكدس (Stack Overflow)، وصعوبة تتبع التنفيذ وفهمه مقارنة بالحلقات، واحتمالية بطء الأداء بسبب الحمل الزائد لاستدعاء الدوال المتكرر، وعدم كفاءته في بعض المسائل مقارنة بالحلول التكرارية.

الشرح: كل استدعاء دالة يضيف إطاراً جديداً إلى مكدس الاستدعاءات (Call Stack) لتخزين معلومات حول الاستدعاء. عند استخدام الاستدعاء الذاتي بشكل مفرط دون وجود شرط توقف مناسب، يمكن أن يمتلئ المكدس، مما يؤدي إلى خطأ تجاوز سعة المكدس. كما أن استدعاءات الدوال المتكررة تحمل تكلفة أداء أعلى من مجرد تكرار كتلة من التعليمات باستخدام حلقة.

تلميح: ما هي الآلية التي تعمل بها الدوال؟ وكيف يمكن للعديد من استدعاءات الدوال أن تؤثر على هذه الآلية؟

اكتب دالة بايثون استدعاء تكراري (Recursive Function) لحساب ثاني أكبر رقم في قائمة غير مرتبة من الأرقام.

الإجابة: python def find_second_largest_recursive(arr): if len(arr) < 2: return None # أو يمكن رفع خطأ # حالة الأساس: إذا كانت القائمة تحتوي على عنصرين فقط if len(arr) == 2: return min(arr) # دعنا نتبع نهجاً يبحث عن أكبر عنصر أولاً ثم ثاني أكبر عنصر # لتجنب تعقيد دالة تكرارية واحدة لحساب ثاني أكبر عنصر مباشرة # سنفترض هنا وجود دالة مساعدة أو نهج مبسط. # الحل التكراري المباشر لـ "ثاني أكبر" معقد جداً. # لتبسيط الأمر، سنستخدم هنا طريقة غير مباشرة تتطلب معالجة إضافية. # الأسلوب الأكثر شيوعاً هو الفرز أو التكرار، لكن لغرض التوضيح التكراري: # مثال توضيحي لفكرة استدعاء تكراري (ليس الحل الأمثل والأكثر كفاءة لـ "ثاني أكبر") # الفكرة هي إيجاد الأكبر، ثم البحث عن الأكبر في الباقي. def find_max(lst): if len(lst) == 1: return lst[0] return max(lst[0], find_max(lst[1:])) max_num = find_max(arr) # الآن، نجد الأكبر في القائمة بعد إزالة جميع تكرارات أكبر رقم # هذا النهج قد لا يكون فعالاً جداً بالاستدعاء الذاتي فقط # ولكنه يوضح استخدام الاستدعاء الذاتي في حل جزء من المشكلة. # لتبسيط الإجابة والتركيز على مفهوم الاستدعاء التكراري: # مثال أبسط: حساب أكبر رقم (أسهل للاستدعاء التكراري) # def find_max_recursive(lst): # if len(lst) == 1: # return lst[0] # return max(lst[0], find_max_recursive(lst[1:])) # max_num = find_max_recursive(arr) # لإيجاد ثاني أكبر رقم بتعقيد مقبول باستخدام الاستدعاء # غالباً ما يتم ذلك عبر تتبع أكبر رقمين أثناء المرور # الذي يمكن تمثيله تكرارياً. # إليك حل يتبع مبدأ الاستدعاء ولكن يتطلب نهجاً مختلفاً قليلاً: # سنتتبع أكبر عنصرين: def find_two_largest(lst, largest, second_largest): if not lst: return second_largest current = lst[0] if current > largest: second_largest = largest largest = current elif current > second_largest and current != largest: second_largest = current return find_two_largest(lst[1:], largest, second_largest) initial_largest = float('-inf') initial_second_largest = float('-inf') # نحتاج لتهيئة أولية مختلفة قليلاً إذا كانت القائمة تحتوي على أقل من عنصرين if len(arr) < 2: return None # لا يوجد ثاني أكبر # تهيئة أولية تعتمد على أول عنصرين if arr[0] > arr[1]: initial_largest = arr[0] initial_second_largest = arr[1] else: initial_largest = arr[1] initial_second_largest = arr[0] # ابدأ الاستدعاء من العنصر الثالث return find_two_largest(arr[2:], initial_largest, initial_second_largest)

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

تلميح: فكر في كيفية معالجة القائمة عن طريق تقسيمها إلى أول عنصر وبقية القائمة، وتتبع أكبر رقمين أثناء هذا التقسيم.

اكتب دالة استدعاء تكراري (Recursive Function) بلغة البايثون لحساب مجموع كل الأرقام الزوجية في قائمة معينة.

الإجابة: python def sum_even_recursive(arr): if not arr: return 0 # حالة الأساس: قائمة فارغة، المجموع صفر first_element = arr[0] rest_of_list = arr[1:] # تحقق إذا كان العنصر الأول زوجياً if first_element % 2 == 0: # إذا كان زوجياً، أضفه إلى مجموع الأرقام الزوجية في بقية القائمة return first_element + sum_even_recursive(rest_of_list) else: # إذا كان فردياً، تجاهله وأوجد مجموع الأرقام الزوجية في بقية القائمة return sum_even_recursive(rest_of_list)

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

تلميح: ما هو الشرط الذي يجب أن يتحقق ليتم تضمين الرقم في المجموع؟ وكيف يمكنك معالجة بقية القائمة بنفس المنطق؟