هل يمكن أن يعود Random.Randint (1،10) من أي وقت مضى 11؟
سؤال
عند البحث عن هذا السؤال وقراءة رمز العجين في random.py
, ، بدأت أتساءل عما إذا كان randrange
و randint
تتصرف حقًا كـ "معلن". أنا أميل كثيرًا إلى تصديق ذلك ، لكن الطريقة التي قرأتها ، randrange
يتم تنفيذها بشكل أساسي
start + int(random.random()*(stop-start))
(بافتراض قيم عدد صحيح ل start
و stop
)، لذا randrange(1, 10)
يجب إعادة رقم عشوائي بين 1 و 9.
randint(start, stop)
يدعو randrange(start, stop+1)
, ، وبالتالي إعادة رقم بين 1 و 10.
سؤالي الآن:
إذا random()
كان من أي وقت مضى للعودة 1.0
, ، ومن بعد randint(1,10)
سيعود 11
, ، أليس كذلك؟
المحلول
من random.py
والمستندات:
"""Get the next random number in the range [0.0, 1.0)."""
ال )
يشير إلى أن الفاصل الزمني حصرية 1.0. هذا هو ، لن يعود 1.0.
هذه اتفاقية عامة في الرياضيات ، [
و ]
شامل ، بينما (
و )
حصري ، ويمكن خلط نوعين من الأقواس (a, b]
أو [a, b)
. القي نظرة على ويكيبيديا: الفاصل الزمني (الرياضيات) للحصول على شرح رسمي.
نصائح أخرى
أشارت إجابات أخرى إلى أن نتيجة random()
دائما بشكل صارم أقل من 1.0
; ؛ ومع ذلك ، هذا فقط نصف القصة.
إذا كنت تحوس randrange(n)
كما int(random() * n)
, ، أنت ايضا بحاجة إلى معرفة ذلك لأي تعويم بيثون x
مرضيه 0.0 <= x < 1.0
, وأي عدد صحيح إيجابي n
, هذا صحيح 0.0 <= x * n < n
, ، لهذا السبب int(x * n)
هو أقل بدقة من n
.
هناك شيئان قد يحدثان خطأ هنا: أولاً ، عندما نحسب x * n
, n
يتم تحويله ضمنيًا إلى تعويم. إلى حد كبير بما فيه الكفاية n
, ، هذا التحويل قد يغير القيمة. ولكن إذا نظرت إلى مصدر Python ، فسترى أنه يستخدم فقط int(random() * n)
طريقة ل n
اصغر من 2**53
(هنا وأسفل أفترض أن النظام الأساسي يستخدم IEEE 754 الزوجي) ، وهو النطاق الذي يحوله تحويله n
إلى تعويم ضمان عدم فقدان المعلومات (لأن n
يمكن تمثيلها بالضبط على أنها تعويم).
الشيء الثاني الذي قد يحدث خطأ هو أن نتيجة الضرب x * n
(التي يتم تنفيذها الآن كمنتج للعوامات ، تذكر) ربما لن يكون ممثلًا تمامًا ، لذلك سيكون هناك بعض التقريب. إذا x
قريب بما فيه الكفاية 1.0
, ، من المتصور أن الدائرية ستدور حول النتيجة n
بحد ذاتها.
لنرى أن هذا لا يمكن أن يحدث ، نحتاج فقط إلى النظر في أكبر قيمة ممكنة x
, ، وهو (على جميع الآلات تقريبًا التي يديرها بيثون) 1 - 2**-53
. لذلك نحن بحاجة إلى إظهار ذلك (1 - 2**-53) * n < n
لعدد عدد صحيح لدينا n
, ، لأنه سيكون صحيحًا دائمًا random() * n <= (1 - 2**-53) * n
.
دليل - إثبات (رسم) دع k
كن صحيحًا فريدًا k
مثل ذلك 2**(k-1) < n <= 2**k
. ثم تطفو التالي لأسفل من n
هو n - 2**(k-53)
. نحن بحاجة إلى إظهار ذلك n*(1-2**53)
(أي ، القيمة الفعلية ، غير الجذابة ، أقرب إلى المنتج) n - 2**(k-53)
من ل n
, ، بحيث يتم تقريبها. لكن القليل من الحساب يظهر أن المسافة من n*(1-2**-53)
إلى n
هو 2**-53 * n
, ، بينما المسافة من n*(1-2**-53)
إلى n - 2**(k-53)
هو (2**k - n) * 2**-53
. ولكن 2**k - n < n
(لأننا اخترنا k
لهذا السبب. 2**(k-1) < n
) ، إذن المنتج هو أقرب الى n - 2**(k-53)
, ، لذلك إرادة الحصول على تقريب لأسفل (على افتراض ، أن المنصة تقوم ببعض أشكال الجولة إلى الخارق).
لذلك نحن آمنون. فوز!
Addendum (2015-07-04): ما سبق يفترض IEEE 754 Binary64 الحساب ، مع وضع التقريب المستدير إلى التعادل. على العديد من الآلات ، هذا الافتراض آمن إلى حد ما. ومع ذلك ، على أجهزة X86 التي تستخدم X87 FPU للنقطة العائمة (على سبيل المثال ، نكهات مختلفة من Linux 32 بت) ، هناك إمكانية لذلك تقريب مزدوج في الضرب ، وهذا يجعل من الممكن random() * n
لجولة فوق إلى n
في الحالة حيث random()
إرجاع أكبر قيمة ممكنة. أصغر مثل n
الذي يمكن أن يحدث هذا هو n = 2049
. انظر المناقشة في http://bugs.python.org/issue24546 للمزيد من.
من وثائق بيثون:
تعتمد جميع وظائف الوحدة النمطية تقريبًا على الوظيفة الأساسية العشوائية () ، والتي تولد تعويمًا عشوائيًا بشكل موحد في نطاق شبه مفتوح [0.0 ، 1.0).
مثل تقريبا كل prng من أرقام التعويم ..