مقدمة في علوم الحاسوب والبرمجة باستخدام Python › المحاضرة 5: الأعداد العشرية العائمة وطرق التقريب 7 / 30 السابق التالي

المحاضرة 5: شرائح الأعداد العشرية وطرق التقريب كاملة

المحاضرة 5: الأعداد العشرية وطرق التقريب (Floats and Approximation Methods)

المصادر والنسبة والترخيص

المادة الأصلية: آنا بيل (Ana Bell)، MIT OpenCourseWare (MIT OCW)، معهد ماساتشوستس للتكنولوجيا، مقرر 6.100L: مقدمة في علوم الحاسوب والبرمجة باستخدام Python، خريف 2022 (Fall 2022).

هذه ترجمة وتكييف عربي غير رسمي وفق رخصة 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؟
a×10−1+b×10−2+c×10−3a\times10^{-1} + b\times10^{-2} + c\times10^{-3}
  • في التمثيل الثنائي نستخدم الفكرة نفسها:
a×2−1+b×2−2+c×2−3a\times2^{-1} + b\times2^{-2} + c\times2^{-3}
  • وبعبارات أبسط: التمثيل الثنائي للكسر العشري ff يتطلب إيجاد قيم aa وbb وcc وغيرها بحيث:
f=0.5a+0.25b+0.125c+0.0625d+0.03125e+…f = 0.5a + 0.25b + 0.125c + 0.0625d + 0.03125e + \dots

الشريحة 6: ماذا عن الكسور؟

  • كيف يمكننا إيجاد ذلك التمثيل؟
  • في الصورة العشرية:
38=0.375=3×10−1+7×10−2+5×10−3\frac{3}{8} = 0.375 = 3\times10^{-1} + 7\times10^{-2} + 5\times10^{-3}
  • فكرة الوصفة (Recipe Idea): إذا استطعنا الضرب في قوة للعدد 2 كبيرة بما يكفي لتحويلها إلى عدد صحيح، فيمكننا التحويل إلى الثنائي، ثم القسمة على القوة نفسها من 2 لاستعادة القيمة.
    • 0.375×(23)=3100.375 \times (2^3) = 3_{10}
    • حوّل 3 إلى ثنائي (وأصبح 11211_2).
    • اقسم على 232^3 (أزح ثلاثة مواضع لليمين) فتحصل على 0.01120.011_2.

الشريحة 7: لكن…

  • إذا لم يوجد عدد صحيح pp بحيث يكون x×(2p)x \times (2^p) عددًا صحيحًا، فإن التمثيل الداخلي يكون دائمًا تقريبًا (Approximation).
  • وأنا أفترض أن تمثيل الكسر العشري الذي قدّمته كمدخل دقيق تمامًا، وليس هو نفسه تقريبًا نتيجةً لإدخال العدد إلى بايثون.
  • التحويل إلى الفاصلة العائمة يعمل:
    • بدقة تامّة لأعداد مثل 3/83/8.
    • لكن ليس لأجل 1/101/10.
  • 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؟
1×10−1+2×10−2+5×10−31\times10^{-1} + 2\times10^{-2} + 5\times10^{-3}
  • لنفترض أننا نريد تمثيله في النظام الثنائي؟
1×2−31\times2^{-3}
  • وماذا عن التمثيل العشري 0.1؟
    • في الأساس 10: 1×10−11 \times 10^{-1}.
    • في الأساس 2: ؟

الشريحة 10: الفكرة؟

  • إذا كان كل شيء في النهاية مُمثَّل في صورة بتات (Bits)، فعلينا التفكير في كيفية استخدام التمثيل الثنائي لالتقاط الأعداد.
  • الأعداد الصحيحة مباشرة.
  • لكن الأعداد الحقيقية (الأشياء التي فيها خانات بعد الفاصلة العشرية) مشكلة.
  • كانت الفكرة هي محاولة تحويل عدد حقيقي إلى عدد صحيح بضربه في مضاعف معيّن من 2 للحصول على عدد صحيح.
  • وأحيانًا لا توجد قوة للعدد 2 من هذا النوع!
  • علينا عندئذٍ أن نقرّب (Approximate) على أيّ حال التسلسل الثنائي اللانهائي المحتمل من البتات اللازم لتمثيلها.

الشريحة 11: الأعداد العشرية (FLOATS)

(FLOATS)

الشريحة 12: تخزين الأعداد العشرية (Storing Floating Point Numbers)

  • الفاصلة العائمة زوج من الأعداد الصحيحة:
#.#
  • الأرقام الدالّة (Significant Digits) وأُسّ الأساس 2 (Base 2 Exponent).
الزوج أي 1×2a1 \times 2^{a} أي التمثيل الثنائي أي القيمة
(1,1)(1, 1) 1×211 \times 2^1 10210_2 2.0
(1,−1)(1, -1) 1×2−11 \times 2^{-1} 0.120.1_2 0.5
(125,−2)(125, -2) 125×2−2125 \times 2^{-2} 11111.01211111.01_2 31.25
  • ملاحظة: العدد 125 هو 111110121111101_2 ثم تُزاح الفاصلة العشرية مسافتين.

الشريحة 13: استخدام مجموعة منتهية من البتات لتمثيل مجموعة محتملة اللانهائية من البتات

  • أقصى عدد من الأرقام الدالّة (Maximum Number of Significant Digits) هو ما يحكم الدقة (Precision) التي يمكن تمثيل الأعداد بها.
  • معظم الحواسيب الحديثة تستخدم 32 بت لتمثيل الأرقام الدالّة.
  • إذا مُثِّل عدد بأكثر من 32 بت في النظام الثنائي، فسيُقرَّب (Rounded) العدد.
  • سيكون الخطأ عند البت رقم 32.
  • سيكون الخطأ فقط من المرتبة 2×10−102 \times 10^{-10}.

الشريحة 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: تقريب x\sqrt{x}

(APPROXIMATE x\sqrt{x})

القيم المبيّنة في المخطط: xx، والتخمينات −2-2، −1-1، 00، 11، 22، 33، 44، 55، 66، 77، 88، مع المدى «جيد بما يكفي» (Good enough).

الشريحة 21: إيجاد الجذور

  • في المحاضرة الماضية نظرنا في استخدام التعداد الشامل/التخمين والاختبار لإيجاد جذور المربعات الكاملة.
  • لنفترض أننا نريد إيجاد الجذر التربيعي لأي عدد صحيح موجب، أو لأي عدد موجب.
  • السؤال: ماذا يعني إيجاد الجذر التربيعي لـ xx؟
    • إيجاد rr بحيث r×r=xr \times r = x؟
    • إذا لم يكن xx مربعًا كاملًا، فليس من الممكن عمومًا إيجاد rr دقيق يحقق هذه العلاقة؛ والبحث الشامل لا نهائي.

الشريحة 22: التقريب (APPROXIMATION)

  • ابحث عن جواب «جيد بما يكفي».
    • مثال: ابحث عن rr بحيث يكون r×rr \times r على مسافة (صغيرة) معطاة من xx.
  • استخدم إبسيلون (ϵ\epsilon، Epsilon): معطى xx نريد إيجاد rr بحيث:
∣r2−x∣<ϵ|r^2 - x| < \epsilon
  • الخوارزمية:
    • ابدأ بتخمين معروف أنه أصغر من اللازم — سمّيه gg.
    • زِدْه بقيمة صغيرة — سمّها aa — لتعطي تخمينًا جديدًا gg.
    • تحقق ممّا إذا كان g2g^2 قريبًا بما يكفي من xx (ضمن ϵ\epsilon).
    • تابع حتى تحصل على جواب قريب بما يكفي من الجواب الفعلي.
  • النظر في كل القيم الممكنة g+kag + k a لقيم kk الصحيحة — لذا هو مشابه للتعداد الشامل.
  • لكن لا يمكن اختبار كل الاحتمالات لأنها لا نهائية.

الشريحة 23: خوارزمية التقريب

  • في هذه الحالة لدينا معاملان نضبطهما (Two Parameters to Set):
    • epsilon: كم نحن قريبون من الجواب؟
    • increment: بمقدار ما نزيد تخميننا؟
  • سيختلف الأداء حسب هذه القيم:
    • في السرعة.
    • في الدقة.
  • تقليل حجم مقدار الزيادة (Decreasing increment size) ← برنامج أبطأ، لكن أكثر احتمالًا للحصول على جواب جيد (والعكس).
القيمة في المخطط
epsilon 4
epsilon 6
epsilon 7
epsilon 8
  • والحدود المبيّنة: −2-2، −1-1، 00، xx، 11، 22، 33، و«جيد بما يكفي» (Good enough).

الشريحة 24: خوارزمية التقريب — أثر زيادة epsilon

  • لدينا معاملان نضبطهما:
    • epsilon: كم نحن قريبون من الجواب؟
    • increment: بمقدار ما نزيد تخميننا؟
  • سيختلف الأداء حسب هذه القيم:
    • في السرعة.
    • في الدقة.
  • زيادة epsilon (Increasing epsilon) ← جواب أقل دقة، لكن برنامج أسرع (والعكس).
القيمة في المخطط
epsilon 4
epsilon 5
epsilon 6
epsilon 7
epsilon 8

الشريحة 25: الفكرة الكبرى

التقريب يشبه التخمين والاختبار، إلا أن:

  1. نزيد بمقدار صغير.
  2. نتوقف عندما نقترب بما يكفي (لأن الدقيق غير ممكن).

الشريحة 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

  • بالنسبة إلى x=36x = 36:
    • لم نجد 6.
    • استغرق نحو 60,000 تخمينًا.
  • لنجرّب:

الشريحة 28: مثال x=54321x = 54321

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.
  • x=54321x = 54321، ومؤشّرا 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) تستخدم الأعداد العشرية:
    • مثل التخمين والاختبار، إلا أن:
      1. نستخدم عددًا عشريًا كمقدار للزيادة.
      2. نتوقف عندما نقترب بما يكفي.
    • لا تستخدم أبدًا == لمقارنة الأعداد العشرية في شرط التوقف.
    • كن حذرًا من تجاوز شرط التوقف «قريب بما يكفي».

الشريحة 37: بيانات MIT OpenCourseWare

MIT OpenCourseWare — https://ocw.mit.edu

6.100L: مقدمة في علوم الحاسوب والبرمجة باستخدام Python.

خريف 2022.

للحصول على معلومات عن الاستشهاد بهذه المواد أو شروط استخدامها، تفضّل بزيارة https://ocw.mit.edu/terms.