المحاضرة 5: شرائح الأعداد العشرية وطرق التقريب كاملة
المحاضرة 5: الأعداد العشرية وطرق التقريب (Floats and Approximation Methods)
المصادر والنسبة والترخيص
المادة الأصلية: آنا بيل (Ana Bell)، MIT OpenCourseWare (MIT OCW)، معهد ماساتشوستس للتكنولوجيا، مقرر 6.100L: مقدمة في علوم الحاسوب والبرمجة باستخدام Python، خريف 2022 (Fall 2022).
- صفحة المحاضرة الرسمية.
- صفحة الشرائح الرسمية.
- ملف الشرائح الأصلي، PDF.
- ملف شيفرة المحاضرة الأصلي.
- تفريغ المحاضرة على OCW (النسخة الإنجليزية الرسمية).
هذه ترجمة وتكييف عربي غير رسمي وفق رخصة CC BY-NC-SA 4.0: النسبة–غير التجاري–المشاركة بالمثل، ولا تعني اعتمادًا أو تأييدًا من MIT أو MIT OCW. شروط الاستخدام والاستشهاد.
منهج الترجمة: لكل صفحة في ملف الشرائح عنوان مستقل ورقم مطابق. نُقلت الأشكال المتكرّرة إلى جداول تحافظ على تسلسلها. الشيفرة محفوظة بالإنجليزية. لم تُضمَّن صور أو صفحات PDF.
الشريحة 1: الأعداد العشرية وطرق التقريب
(FLOATS and APPROXIMATION METHODS)
(نزّل الشرائح وملفات .py لمتابعة الشرح).
6.100L، المحاضرة 5 — آنا بيل (Ana Bell).
الشريحة 2: دافعنا من المحاضرة الماضية
x = 0
for i in range(10):
x += 0.1
print(x == 1)
print(x, '==', 10*0.1)
الشريحة 3: الأعداد الصحيحة
- الأعداد الصحيحة لها تمثيلات مباشرة (Straightforward) في النظام الثنائي.
- كانت الشيفرة بسيطة (ويمكن إضافة جزء للتعامل مع الأعداد السالبة):
if num < 0:
is_neg = True
num = abs(num)
else:
is_neg = False
result = ''
if num == 0:
result = '0'
while num > 0:
result = str(num%2) + result
num = num//2
if is_neg:
result = '-' + result
الشريحة 4: الكسور
(FRACTIONS)
الشريحة 5: الكسور
- ماذا يعني الكسر العشري (Decimal Fraction)
0.abc؟
- في التمثيل الثنائي نستخدم الفكرة نفسها:
- وبعبارات أبسط: التمثيل الثنائي للكسر العشري يتطلب إيجاد قيم و و وغيرها بحيث:
الشريحة 6: ماذا عن الكسور؟
- كيف يمكننا إيجاد ذلك التمثيل؟
- في الصورة العشرية:
- فكرة الوصفة (Recipe Idea): إذا استطعنا الضرب في قوة للعدد 2 كبيرة بما يكفي لتحويلها إلى عدد صحيح، فيمكننا التحويل إلى الثنائي، ثم القسمة على القوة نفسها من 2 لاستعادة القيمة.
- حوّل 3 إلى ثنائي (وأصبح ).
- اقسم على (أزح ثلاثة مواضع لليمين) فتحصل على .
الشريحة 7: لكن…
- إذا لم يوجد عدد صحيح بحيث يكون عددًا صحيحًا، فإن التمثيل الداخلي يكون دائمًا تقريبًا (Approximation).
- وأنا أفترض أن تمثيل الكسر العشري الذي قدّمته كمدخل دقيق تمامًا، وليس هو نفسه تقريبًا نتيجةً لإدخال العدد إلى بايثون.
- التحويل إلى الفاصلة العائمة يعمل:
- بدقة تامّة لأعداد مثل .
- لكن ليس لأجل .
- workpiece الأول له قوة للعدد 2 تحوّله إلى عدد صحيح، والآخر ليس له.
الشريحة 8: تتبّع ذلك بنفسك
- رابط Python Tutor.
x = 0.625
p = 0
while ((2**p)*x)%1 != 0:
print('Remainder = ' + str((2**p)*x - int((2**p)*x)))
p += 1
num = int(x*(2**p))
result = ''
if num == 0:
result = '0'
while num > 0:
result = str(num%2) + result
num = num//2
for i in range(p - len(result)):
result = '0' + result
result = result[0:-p] + '.' + result[-p:]
print('The binary representation of the decimal ' + str(x) + ' is ' + str(result))
الشريحة 9: لماذا هذه مشكلة؟
- ماذا يعني التمثيل العشري
0.125؟
- لنفترض أننا نريد تمثيله في النظام الثنائي؟
- وماذا عن التمثيل العشري
0.1؟- في الأساس 10: .
- في الأساس 2: ؟
الشريحة 10: الفكرة؟
- إذا كان كل شيء في النهاية مُمثَّل في صورة بتات (Bits)، فعلينا التفكير في كيفية استخدام التمثيل الثنائي لالتقاط الأعداد.
- الأعداد الصحيحة مباشرة.
- لكن الأعداد الحقيقية (الأشياء التي فيها خانات بعد الفاصلة العشرية) مشكلة.
- كانت الفكرة هي محاولة تحويل عدد حقيقي إلى عدد صحيح بضربه في مضاعف معيّن من 2 للحصول على عدد صحيح.
- وأحيانًا لا توجد قوة للعدد 2 من هذا النوع!
- علينا عندئذٍ أن نقرّب (Approximate) على أيّ حال التسلسل الثنائي اللانهائي المحتمل من البتات اللازم لتمثيلها.
الشريحة 11: الأعداد العشرية (FLOATS)
(FLOATS)
الشريحة 12: تخزين الأعداد العشرية (Storing Floating Point Numbers)
- الفاصلة العائمة زوج من الأعداد الصحيحة:
#.#
- الأرقام الدالّة (Significant Digits) وأُسّ الأساس 2 (Base 2 Exponent).
| الزوج | أي | أي التمثيل الثنائي | أي القيمة |
|---|---|---|---|
| 2.0 | |||
| 0.5 | |||
| 31.25 |
- ملاحظة: العدد 125 هو ثم تُزاح الفاصلة العشرية مسافتين.
الشريحة 13: استخدام مجموعة منتهية من البتات لتمثيل مجموعة محتملة اللانهائية من البتات
- أقصى عدد من الأرقام الدالّة (Maximum Number of Significant Digits) هو ما يحكم الدقة (Precision) التي يمكن تمثيل الأعداد بها.
- معظم الحواسيب الحديثة تستخدم 32 بت لتمثيل الأرقام الدالّة.
- إذا مُثِّل عدد بأكثر من 32 بت في النظام الثنائي، فسيُقرَّب (Rounded) العدد.
- سيكون الخطأ عند البت رقم 32.
- سيكون الخطأ فقط من المرتبة .
الشريحة 14: نتائج مذهلة!
x = 0
for i in range(10):
x += 0.125
print(x == 1.25)
x = 0
for i in range(10):
x += 0.1
print(x == 1)
print(x, '==', 10*0.1)
الشريحة 15: درس القصة (Moral of the Story)
- لا تستخدم أبدًا
==لاختبار الأعداد العشرية. - بدلًا من ذلك، اختبر ما إذا كانا متقاربين بمقدار صغير.
- ما يُطبع ليس دائمًا ما هو مخزَّن في الذاكرة.
- يجب أن تكون حذرًا في تصميم الخوارزميات التي تستخدم الأعداد العشرية.
الشريحة 16: طرق التقريب (APPROXIMATION METHODS)
(APPROXIMATION METHODS)
الشريحة 17: المحاضرة الماضية
- التخمين والاختبار يوفّر خوارزمية بسيطة لحل المسائل.
- عندما تكون مجموعة الحلول المحتملة قابلة للتعداد، فإن التعداد الشامل مضمون أنه سينجح (في النهاية).
- لكنها طريقة محدودة لحل المسائل (A Limiting Way).
- مقدار الزيادة (Increment) يكون عادةً عددًا صحيحًا لكن ليس دائمًا؛ أي أننا نحتاج فقط إلى بعض النمط (Pattern) يمنحنا مجموعة منتهية من القيم القابلة للتعداد.
- لا يمكنها أن تعطينا حلًا تقريبيًا (Approximate Solution) بدرجات متفاوتة.
الشريحة 18: أفضل من التخمين والاختبار
- نريد إيجاد تقريب (Approximation) لجواب.
- ليس الجواب الصحيح فقط، كما في التخمين والاختبار.
- وليس أيضًا مجرد أننا لم نجد الجواب، كما في التخمين والاختبار.
الشريحة 19: أثر التقريب على خوارزمياتنا؟
- قد لا يكون الجواب الدقيق متاحًا.
- نحتاج إلى إيجاد طرق للحصول على جواب «جيد بما يكفي».
- جوابنا «قريب بما يكفي» من الجواب المثالي.
- نحتاج إلى طرق للتعامل مع الحقيقة أن التعداد الشامل لا يستطيع اختبار كل قيمة ممكنة، لأن مجموعة الأجوبة الممكنة لا نهائية من حيث المبدأ.
- أخطاء التقريب في الفاصلة العائمة مهمة لأجل هذه الطريقة.
- لا يمكنك الاعتماد على التساوي!
الشريحة 20: تقريب
(APPROXIMATE )
القيم المبيّنة في المخطط: ، والتخمينات ، ، ، ، ، ، ، ، ، ، ، مع المدى «جيد بما يكفي» (Good enough).
الشريحة 21: إيجاد الجذور
- في المحاضرة الماضية نظرنا في استخدام التعداد الشامل/التخمين والاختبار لإيجاد جذور المربعات الكاملة.
- لنفترض أننا نريد إيجاد الجذر التربيعي لأي عدد صحيح موجب، أو لأي عدد موجب.
- السؤال: ماذا يعني إيجاد الجذر التربيعي لـ ؟
- إيجاد بحيث ؟
- إذا لم يكن مربعًا كاملًا، فليس من الممكن عمومًا إيجاد دقيق يحقق هذه العلاقة؛ والبحث الشامل لا نهائي.
الشريحة 22: التقريب (APPROXIMATION)
- ابحث عن جواب «جيد بما يكفي».
- مثال: ابحث عن بحيث يكون على مسافة (صغيرة) معطاة من .
- استخدم إبسيلون (، Epsilon): معطى نريد إيجاد بحيث:
- الخوارزمية:
- ابدأ بتخمين معروف أنه أصغر من اللازم — سمّيه .
- زِدْه بقيمة صغيرة — سمّها — لتعطي تخمينًا جديدًا .
- تحقق ممّا إذا كان قريبًا بما يكفي من (ضمن ).
- تابع حتى تحصل على جواب قريب بما يكفي من الجواب الفعلي.
- النظر في كل القيم الممكنة لقيم الصحيحة — لذا هو مشابه للتعداد الشامل.
- لكن لا يمكن اختبار كل الاحتمالات لأنها لا نهائية.
الشريحة 23: خوارزمية التقريب
- في هذه الحالة لدينا معاملان نضبطهما (Two Parameters to Set):
epsilon: كم نحن قريبون من الجواب؟increment: بمقدار ما نزيد تخميننا؟
- سيختلف الأداء حسب هذه القيم:
- في السرعة.
- في الدقة.
- تقليل حجم مقدار الزيادة (Decreasing increment size) ← برنامج أبطأ، لكن أكثر احتمالًا للحصول على جواب جيد (والعكس).
| القيمة في المخطط | |
|---|---|
epsilon |
4 |
epsilon |
6 |
epsilon |
7 |
epsilon |
8 |
- والحدود المبيّنة: ، ، ، ، ، ، ، و«جيد بما يكفي» (Good enough).
الشريحة 24: خوارزمية التقريب — أثر زيادة epsilon
- لدينا معاملان نضبطهما:
epsilon: كم نحن قريبون من الجواب؟increment: بمقدار ما نزيد تخميننا؟
- سيختلف الأداء حسب هذه القيم:
- في السرعة.
- في الدقة.
- زيادة
epsilon(Increasing epsilon) ← جواب أقل دقة، لكن برنامج أسرع (والعكس).
| القيمة في المخطط | |
|---|---|
epsilon |
4 |
epsilon |
5 |
epsilon |
6 |
epsilon |
7 |
epsilon |
8 |
الشريحة 25: الفكرة الكبرى
التقريب يشبه التخمين والاختبار، إلا أن:
- نزيد بمقدار صغير.
- نتوقف عندما نقترب بما يكفي (لأن الدقيق غير ممكن).
الشريحة 26: التنفيذ (IMPLEMENTATION)
x = 36
epsilon = 0.01
num_guesses = 0
guess = 0.0
increment = 0.0001
while abs(guess**2 - x) >= epsilon:
guess += increment
num_guesses += 1
print('num_guesses =', num_guesses)
print(guess, 'is close to square root of', x)
الشريحة 27: ملاحظات بقيم مختلفة لـ x
- بالنسبة إلى :
- لم نجد 6.
- استغرق نحو 60,000 تخمينًا.
- لنجرّب:
-
الشريحة 28: مثال
x = 54321
epsilon = 0.01
numGuesses = 0
guess = 0.0
increment = 0.0001
while abs(guess**2 - x) >= epsilon:
guess += increment
numGuesses += 1
if numGuesses%100000 == 0:
print('Current guess =', guess)
print('Current guess**2 - x =', abs(guess*guess - x))
print('numGuesses =', numGuesses)
print(guess, 'is close to square root of', x)
الشريحة 29: تجاوزنا إبسيلون!
(WE OVERSHOT the EPSILON!)
- السهم الأزرق (Blue arrow) هو التخمين.
- السهم الأخضر (Green arrow) هو
guess**2. - ، ومؤشّرا
epsilonحول القيمة المقاربة.
الشريحة 30: بعض الملاحظات
- دالة التناقص (Decrementing Function) تبدأ في النهاية بالتزايد.
- لذلك لم تخرج الحلقة كما هو متوقّع.
- لقد تجاوزنا العلامة (Overshot the Mark).
- أي أننا قفزنا من قيمة بعيدة جدًا لكن صغيرة، إلى قيمة بعيدة جدًا لكن كبيرة.
- لم نأخذ هذه الاحتمالية في الحسبان عند كتابة الحلقة.
- لنصلح ذلك.
الشريحة 31: لنصلحها
x = 54321
epsilon = 0.01
numGuesses = 0
guess = 0.0
increment = 0.0001
while abs(guess**2 - x) >= epsilon and guess**2 <= x:
guess += increment
numGuesses += 1
print('numGuesses =', numGuesses)
if abs(guess**2 - x) >= epsilon:
print('Failed on square root of', x)
else:
print(guess, 'is close to square root of', x)
الشريحة 32: الفكرة الكبرى
من الممكن تجاوز إبسيلون، لذلك تحتاج إلى شرط انتهاء آخر.
الشريحة 33: بعض الملاحظات
- الآن تتوقف، لكنها تُبلّغ عن الفشل، لأنها تجاوزت الجواب.
- لنجرّب ضبط
incrementعلى0.00001. - مقدار زيادة أصغر يعني أن قيمًا أكثر سيتم فحصها.
- سيكون البرنامج أبطأ.
الشريحة 34: الفكرة الكبرى
كن حذرًا عند مقارنة الأعداد العشرية.
الشريحة 35: الدروس المستفادة في التقريب
- لا يمكنك استخدام
==للتحقق من شرط الخروج. - يجب أن تكون حذرًا من أن تقفز آلية التكرار فوق اختبار الخروج فتستمر الحلقة إلى الأبد.
- توجد مقايضة (Tradeoff) بين كفاءة الخوارزمية ودقة النتيجة.
- يجب أن تفكر في مدى قرب الجواب الذي تريده عند ضبط معاملات الخوارزمية.
- للحصول على جواب جيد، قد تكون هذه الطريقة بطيئة مؤلمة.
- هل هناك طريقة أسرع تظل تعطي أجوبة جيدة؟
- نعم! سنراها في المحاضرة القادمة…
الشريحة 36: ملخص
- الأعداد العشرية (Floating Point Numbers) تقدّم تحديات!
- لا يمكن تمثيلها في الذاكرة بدقة تامّة.
- العمليات على الأعداد العشرية تُدخل أخطاء ضئيلة.
- العمليات المتعددة على الأعداد العشرية تُضخّم الأخطاء. :(
- طرق التقريب (Approximation Methods) تستخدم الأعداد العشرية:
- مثل التخمين والاختبار، إلا أن:
- نستخدم عددًا عشريًا كمقدار للزيادة.
- نتوقف عندما نقترب بما يكفي.
- لا تستخدم أبدًا
==لمقارنة الأعداد العشرية في شرط التوقف. - كن حذرًا من تجاوز شرط التوقف «قريب بما يكفي».
- مثل التخمين والاختبار، إلا أن:
الشريحة 37: بيانات MIT OpenCourseWare
MIT OpenCourseWare — https://ocw.mit.edu
6.100L: مقدمة في علوم الحاسوب والبرمجة باستخدام Python.
خريف 2022.
للحصول على معلومات عن الاستشهاد بهذه المواد أو شروط استخدامها، تفضّل بزيارة https://ocw.mit.edu/terms.