σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academyخرائط الطريقالمسارات
Ctrl K
AO
+1.2k متعلّم
تسجيل الدخولانضم ↗
خرائط الطريقالمسارات
AO
+1.2k متعلّم
تسجيل الدخول
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academyخرائط الطريقالمسارات
Ctrl K
AO
+1.2k متعلّم
تسجيل الدخولانضم ↗
خرائط الطريقالمسارات
AO
+1.2k متعلّم
تسجيل الدخول
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
مسار Lock-free›الوحدة 1 · حالات التسابق والمستكشف›الموضوع 1.2·~1 سمتقدم

مستكشف التداخلات: العثور على الأخطاء بتعداد الجداول

الموضع في المسارالوحدة 1 من 4 · الموضوع 2 من 2
1.2 مستكشف التداخلات: العثور على الأخطاء بتعداد الجداول
—درجة المختبر— / 100إجمالي XP—التتابع—

4 دقيقة قراءة4 أمثلة برمجية

  1. 01كم عدد الجداول؟
  2. 02الخيوط كمولّدات
  3. 03استكشف ثم اسأل
  4. 04ما ستبنيه

تجربة الجداول يدويًا لا تجد الخطأ إلا إن خمّنت الجدول الصحيح. يشغّل المستكشف البرنامج تحت كل الجداول ويسرد كل النتائج ويعطيك الجدول الذي كسره. وفي حالة خيطين يعدّان أيام صعود عشرة عوائد من SPY هناك 210 جدولًا لا يعطي العدّ الصحيح منها إلا 10.

01

كم عدد الجداول؟

الجدول ترتيب للأدوار والخيط t يأخذ منها lengths[t] بالضبط. لعدّ الترتيبات نضع كل الأدوار في صف (مجموع أطوالها) ويمكن ترتيبه بعدد من الطرق يساوي مضروب ذلك العدد؛ ثم نقسم على مضروب أدوار كل خيط لأن تبديل دورين من الخيط نفسه لا يعطي جدولًا جديدًا. هذا هو المعامل متعدد الحدود. ينمو بسرعة: لخيطين من دورين 6 جداول فقط ولثلاثة خيوط من ثلاثة أدوار 1680 ولخيطين من عشرة أدوار 184756. ولهذا لا يستطيع أحد فحص برنامج متزامن بتجربة جداول عشوائية والأمل.

main.pyPython
def count_schedules(lengths):    total = factorial(sum(lengths))    for n in lengths:        total //= factorial(n)    return totalprint(count_schedules([2, 2]))print(count_schedules([3, 3, 3]))print(count_schedules([10, 10]))
6
1680
184756
02

الخيوط كمولّدات

في المختبر السابق كان الخيط قائمة خطوات. والشيفرة الحقيقية أفضل كتابتها بـPython عادية، لذا يصبح الخيط من الآن مولّدًا يأخذ القاموس المشترك وينفّذ yield قبل كل وصول إلى البيانات المشتركة. فاستدعاء واحد لـnext() يقدّم الخيط حتى وصوله التالي لا أبعد، وهكذا يكون الدور الواحد خطوة واحدة بالضبط يمكن لخيط آخر أن يتداخل معها. تهيّئ run_threads كل مولّد (تشغّله حتى أول yield) وتعطي دورًا للخيط الذي يسميه كل مدخل في الجدول وحين ينتهي الجدول تترك الخيوط غير المنتهية تكمل بترتيب الخيوط. وتسرد all_schedules كل ترتيبات الأدوار باختيار الخيط الذي يبدأ ثم التكرار على الباقي. أدناه الجداول الستة لخيطين يزيد كل منهما مرة واحدة.

main.pyPython
def all_schedules(lengths):    if sum(lengths) == 0:        return [[]]    out = []    for tid, n in enumerate(lengths):        if n:            rest = list(lengths)            rest[tid] -= 1            out += [[tid] + tail for tail in all_schedules(rest)]    return outdef run_threads(make_threads, schedule, shared=None):    shared = dict(shared or {})    threads = make_threads(shared)    alive = [True] * len(threads)    def turn(tid):        try:            next(threads[tid])        except StopIteration:            alive[tid] = False    for tid in range(len(threads)):        turn(tid)    for tid in schedule:        if alive[tid]:            turn(tid)    while any(alive):        for tid in range(len(threads)):            if alive[tid]:                turn(tid)    return sharedtwo = lambda shared: [increments(1)(shared), increments(1)(shared)]for schedule in all_schedules([2, 2]):    print(schedule, run_threads(two, schedule))
[0, 0, 1, 1] {'x': 2}
[0, 1, 0, 1] {'x': 1}
[0, 1, 1, 0] {'x': 1}
[1, 0, 0, 1] {'x': 1}
[1, 0, 1, 0] {'x': 1}
[1, 1, 0, 0] {'x': 2}

تنتهي اثنتان من الجداول الستة عند 2 والأربعة الأخرى عند 1. الجيدان هما ما ينفّذ فيه خيط خطوتيه قبل أن يبدأ الآخر. وفي الأربعة الأخرى يقرأ الخيطان قبل أن يكتب أيٌّ منهما وهذا بالضبط التحديث الضائع من المختبر السابق وقد وُجد الآن دون تخمين.

03

استكشف ثم اسأل

تشغّل explore البرنامج تحت كل الجداول وتعدّ كم جدولًا يعطي كل حالة نهائية. المفتاح هو صف مرتّب من أزواج (الاسم، القيمة) لتكون الحالات المتساوية المفتاح نفسه. وتطرح find_schedule سؤال تقرير الخطأ: أي جدول يكسر ثابتي؟ تعيد الأول وبما أن run_threads حتمية يمكنك إعادة تشغيله أو طباعته أو لصقه في اختبار. وإن أعادت None فالثابت صحيح تحت كل الجداول وهذا إثبات لهذا البرنامج الصغير لا أمل.

main.pyPython
def explore(make_threads, lengths, shared=None):    outcomes = {}    for schedule in all_schedules(lengths):        key = tuple(sorted(run_threads(make_threads, schedule, shared).items()))        outcomes[key] = outcomes.get(key, 0) + 1    return outcomesdef find_schedule(make_threads, lengths, check, shared=None):    for schedule in all_schedules(lengths):        if not check(run_threads(make_threads, schedule, shared)):            return schedule    return Noneprint(explore(two, [2, 2]))print(find_schedule(two, [2, 2], lambda shared: shared["x"] == 2))
{(('x', 2),): 2, (('x', 1),): 4}
[0, 1, 0, 1]
الفضاء ينمو بشكل مضروبي

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

main.pyPython
returns = [float(r) for r in load_bars()["close"].pct_change().dropna()][:10]ups = [sum(1 for r in returns[start::2] if r > 0) for start in (0, 1)]lengths = [2 * k for k in ups]many = lambda shared: [increments(k)(shared) for k in ups]print(ups, count_schedules(lengths))print({dict(key)["x"]: n for key, n in explore(many, lengths).items()})
[2, 3] 210
{5: 10, 4: 70, 3: 104, 2: 26}
ما تقوله البيانات الحقيقيةأول 10 عوائد لـSPY، خيطان يعدّان أيام الصعود
الجداول210لـ2 و3 أيام صعود
الجواب الصحيح10جداول تنتهي عند x = 5
تضيّع تحديثات200من 210 جدولًا
أسوأ حالة2قيمة x في النهاية من 5
الجداول حسب القيمة النهائية لـx
26
104
70
10

الجواب الصحيح x = 5 يظهر في 10 جداول فقط من 210 أي نحو 5%. اختبار يشغّل البرنامج مرة أو مئة مرة على جهاز حقيقي لا يتداخل فيه الخيطان هكذا إلا نادرًا سيمرّ تقريبًا في كل مرة ومع ذلك يُسلَّم بخطأ. والمستكشف لا يفوّته وfind_schedule تعطيك أول مذنب: [0, 0, 0, 1, 0, 1, 1, 1, 1, 1].

تعريفالثابت

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

04

ما ستبنيه

يتبع المختبر الدرس: عدد الجداول وقائمة كلها والآلة التي تشغّل الخيوط المولّدة تحت جدول والمستكشف الذي يعدّ النتائج والبحث عن أول جدول يكسر ثابتًا ودراسة خيطين يعدّان أيام صعود SPY.

  • الدالة · السؤالتعتمد على
  • count_schedules(lengths)كم جدولًا للبرنامج؟حساب بسيط
  • all_schedules(lengths)ما هي بالترتيب؟حساب بسيط
  • run_threads(make_threads, schedule, shared)ماذا يترك جدول واحد؟حساب بسيط
  • explore(make_threads, lengths, shared)ما النتائج الموجودة وكم مرة؟all_schedulesrun_threads
  • find_schedule(make_threads, lengths, check)أي جدول يكسر الثابت أولًا؟all_schedulesrun_threads
  • study(bars)كم جدولًا من عدّ SPY يفشل؟count_schedulesexplorefind_schedule

أهم الخلاصات

  • عدّ الجداول أولًا. ينمو المعامل متعدد الحدود بشكل مضروبي: 6 جداول لخيطين من دورين و184756 لخيطين من عشرة.
  • كل yield يعلّم وصولًا مشتركًا. كتابة الخيوط كمولّدات تجعل الدور وصولًا واحدًا فيستطيع المستكشف تجربة كل تداخلات شيفرة Python العادية.
  • ثابت مع كل الجداول إثبات. 10 جداول فقط من 210 أعطت العدّ الصحيح؛ وfind_schedule تعيد أول ما لم يفعل جاهزًا للإعادة.
تحقق سريع
يأخذ خيطان 3 أدوار لكل منهما. كم جدولًا مختلفًا لهما؟
اختر إجابة.

الموضوع التالي: يُظهر المستكشف أن الخطأ حقيقي؛ والآن نصلحه. يضيف الذرّيات والقراءة والتعديل والكتابة العمليات التي يستطيع المعالج تنفيذها في خطوة واحدة غير قابلة للتجزئة مثل fetch-and-add وcompare-and-swap وتتحقق بالمستكشف أن العدّاد صحيح تحت كل الجداول.

المختبر: ما الذي يُقيَّم

6 مهام · 100 نقطة
  • كم جدولًا10 نقطة
  • كل جدول15 نقطة
  • تشغيل الخيوط25 نقطة
  • عدّ كل نتيجة20 نقطة
  • أول جدول سيئ10 نقطة
  • الدراسة على جدول SPY20 نقطة
المستكشف · lab-lf-02
▾ lab-lf-02
▾ data
…
bash · داخل المتصفح
learner@quantcraft:~/lab-lf-02$
لم يتم تحميل Python بعدPythonLn 1, Col 1Spaces: 4UTF-8

هل تريد إعادة ضبط هذا الموضوع؟

سيعود كل ما في lab-lf-02 إلى الملفات الابتدائية وستُمسح الطرفية. ستفقد حلّك الحالي ولا يمكن التراجع عن ذلك.

سيُعاد ضبطه
  • main.py ← الشيفرة الابتدائية
  • سجل الطرفية ومخرجات الاختبارات
  • التغييرات غير المُرسلة
سيُحتفظ به
  • أفضل درجة في المختبر
  • نقاط XP والتتابع
  • تقدّم المحاضرة والملاحظات