دو نسخهٔ سرویس ممکن است روی همان کار، مدت‌های متفاوتی بدهند؛ اما یک میانگینِ کوچک‌تر هنوز توضیح نمی‌دهد چه مقایسه‌ای انجام شده است. در این درس، مدت‌های تألیفیِ جفت‌شده را با واحد میلی‌ثانیه نگه می‌داریم و برای یک فرضِ صریح، آزمونِ دقیقِ تغییر علامت می‌سازیم. اندازهٔ اختلاف و احتمالِ دنباله جدا گزارش می‌شوند. هیچ درخواست شبکه، زمان‌سنجی، آزمایش تصادفیِ واقعی یا اندازه‌گیریِ محصول در این مثال انجام نشده است.

۱. مقایسه را با جفتِ درست و واحدِ روشن شروع کنیم

جابه‌جا شدنِ جفت‌ها، پرسشِ آزمایش را عوض می‌کند
تصویر مفهومی: جابه‌جا شدنِ جفت‌ها، پرسشِ آزمایش را عوض می‌کند؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

هر ردیف، شناسهٔ یک مورد و مدتِ نسخه‌های A و B را دارد. جفت باید به همان بارِ کاری و شرایطِ تعریف‌شده مربوط باشد؛ صرفِ برابریِ طولِ دو فهرست این رابطه را ثابت نمی‌کند. مرزِ مدت نیز مهم است: زمانِ صف، پردازش و انتقال را باید از ابتدا تعریف کرد. دادهٔ کنونی فقط عددهای ساختگی است و ادعا نمی‌کند این مرزها در یک سرویس مشاهده شده‌اند.

شش مدتِ A برابر ۱۲، ۱۵، ۱۴، ۱۱، ۱۶ و ۱۳ و مدت‌های B برابر ۱۰، ۱۲، ۱۳، ۹، ۱۵ و ۱۲ میلی‌ثانیه‌اند. هر دو مقدارِ هم‌ردیف یک جفت هستند. اختلاف را A منهای B می‌گیریم؛ مثبت بودن یعنی B در همان داده مدتِ کمتری دارد. این آمارهٔ میانگین، همان صدکِ تأخیر نیست و دربارهٔ P95 یا درخواستِ بعدی حکم نمی‌دهد.

راهنمای مشاهده‌های جفت‌شدهٔ NIST، تناظرِ یک‌به‌یک و محاسبهٔ اختلافِ داخلِ هر جفت را توضیح می‌دهد. آن صفحه آزمونِ t جفت‌شده هم دارد؛ کدِ حاضر آن آزمون را اجرا نمی‌کند. شناسه‌های کوتاهِ برنامه برای جلوگیری از تکرارِ ردیف‌اند، نه گواهِ همسان‌بودنِ واقعیِ دو درخواست یا مستقل‌بودنِ جفت‌ها.

۲. فرضِ تغییر علامت از کجا می‌آید؟

جابه‌جاییِ برچسب‌ها فقط زیرِ فرضِ مشخص مجاز است
تصویر مفهومی: جابه‌جاییِ برچسب‌ها فقط زیرِ فرضِ مشخص مجاز است؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

فرضِ عددیِ ما چنین است: مشروط به اندازهٔ اختلاف‌های مشاهده‌شده، تمامِ ترکیب‌های علامتِ مستقل و منصفانه، وزنِ برابر دارند. برای n جفت، دو به توان n انتساب داریم. عوض‌کردنِ برچسب‌های A و B در یک جفت، علامتِ اختلافش را برمی‌گرداند. میانگینِ صفر به‌تنهایی این تقارن را ایجاد نمی‌کند؛ دو فهرستِ دلخواهِ «قبل» و «بعد» نیز خودکار این فرض را ندارند.

یک توجیهِ آزمایشی می‌تواند تخصیصِ تصادفیِ مستقلِ دو برچسب درونِ هر جفت باشد، زیرِ فرضِ نبودِ اثر در همهٔ واحدها و بدونِ تداخل یا اثرِ انتقالی میان اجراها. اینجا چنین آزمایشی انجام نداده‌ایم؛ فقط حسابِ مدل را نشان می‌دهیم. گرم‌شدنِ کش، ترتیبِ اجرا، تغییرِ بار و وابستگیِ چند ردیف به یک رخداد، می‌توانند تفسیرِ ساده را نامناسب کنند.

بخشِ طرح‌های بلوکیِ تصادفیِ NIST، ثابت‌نگه‌داشتنِ عواملِ مزاحمِ قابل‌کنترل و نقشِ تصادفی‌سازی را توضیح می‌دهد. در مدلِ جمعی، یک مؤلفهٔ مشترکِ زمان داخلِ جفت از اختلاف حذف می‌شود؛ اما این جبر، همهٔ عواملِ مزاحم را شناسایی یا حذف نمی‌کند. کد هیچ بررسیِ تجربی برای صحتِ تخصیص، عدمِ تداخل یا استقلال انجام نمی‌دهد.

در مستندِ کاملِ permutation_test، جابه‌جاییِ نمونه‌ها درونِ جفت با تغییرِ خودِ جفت‌بندی یا مخلوط‌کردنِ نمونه‌های مستقل فرق دارد. ما تعریفِ مربوط به samples را برای دو نسخه به حسابِ علامت تبدیل می‌کنیم. SciPy در برنامه وارد نمی‌شود و بازتولیدِ همهٔ گزینه‌ها یا تلورانس‌های آن را ادعا نمی‌کنیم.

۳. اختلافِ میانگین و احتمالِ دنباله را جدا بخوانیم

اندازهٔ اثر و احتمالِ دنباله، دو کمیتِ جدا هستند
تصویر مفهومی: اندازهٔ اثر و احتمالِ دنباله، دو کمیتِ جدا هستند؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

اختلاف‌های نمونه دو، سه، یک، دو، یک و یک میلی‌ثانیه‌اند. جمع ده و میانگین پنج‌سوم میلی‌ثانیه می‌شود. آمارهٔ آزمون قدرمطلقِ همین جمع است؛ تقسیمِ مشترک بر شش ترتیب را عوض نمی‌کند. در ۶۴ انتساب، فقط حالتِ همه‌مثبت و همه‌منفی، جمعی با قدرمطلقِ دست‌کم ده می‌دهند. پس احتمالِ دنبالهٔ دوطرفه در قراردادِ ما یک‌سی‌ودوم است.

برابری با آمارهٔ مشاهده‌شده هم داخلِ دنباله است. انتساب‌های متفاوت ممکن است یک عدد بدهند و وزنشان باید باقی بماند. برای اختلاف‌های یک و یک، جمع‌های منفیِ دو، صفر، صفر و دو داریم؛ احتمالِ دوطرفه یک‌دوم است. تبدیلِ خروجی به مجموعهٔ سه عددِ یکتا و وزن‌دادنِ برابر، به‌اشتباه دو‌سوم می‌دهد. اختلافِ صفر هم دو انتخابِ برچسب دارد، هرچند حاصلشان یکسان است.

توزیعِ علامتِ این مدل متقارن است؛ احتمالِ قدرمطلق با دو برابرِ دنبالهٔ یک‌طرفهٔ کوچک‌تر و سقفِ یک سازگار می‌شود. این تعریف را به هر آزمونِ جایگشتی تعمیم نمی‌دهیم. حالت‌های کامل شمرده شده‌اند و اصلاحِ یک‌واحدیِ شمارنده و مخرجِ آزمونِ تصادفیِ تقریبی را اینجا اضافه نمی‌کنیم. مقدارِ یک‌سی‌ودوم، احتمالِ درست‌بودنِ فرضِ صفر یا درصدِ بهبودِ سرویس نیست.

۴. کدِ کامل و مرجعِ مستقلِ توزیع

دو مسیرِ مستقل، توزیعِ وزن‌دار را بررسی می‌کنند
تصویر مفهومی: دو مسیرِ مستقل، توزیعِ وزن‌دار را بررسی می‌کنند؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

فایل را با نام paired_latency_randomization.py اجرا کنید. فقط کتابخانهٔ استاندارد لازم است. رابطِ آموزشی یک تا دوازده ردیفِ تاپل، شناسهٔ یکتا و مدت‌های صحیحِ صفر تا ده‌هزار میلی‌ثانیه می‌پذیرد. کرانِ دوازده برای شمارشِ کاملِ حداکثر ۴۰۹۶ انتساب است؛ حدِ عمومیِ آزمایش یا مشخصاتِ ابزار نیست. حسابِ صحیح و Fraction، تساوی‌های همین داده را بدونِ تلورانسِ شناور می‌سنجند.

"""Conditional exact paired sign null on synthetic durations; no benchmark."""
from fractions import Fraction
from itertools import product
import json
import platform
import re


PAIRS_MS = (('case-a', 12, 10), ('case-b', 15, 12), ('case-c', 14, 13),
            ('case-d', 11, 9), ('case-e', 16, 15), ('case-f', 13, 12))


def checked_pairs(records):
    if type(records) is not tuple or not 1 <= len(records) <= 12:
        raise ValueError('one to twelve exact tuple records required')
    identifiers = set()
    differences = []
    for row in records:
        if type(row) is not tuple or len(row) != 3:
            raise ValueError('exact tuple(case_id, A_ms, B_ms) required')
        identifier, a, b = row
        if type(identifier) is not str or re.fullmatch(r'[A-Za-z0-9_-]{1,16}', identifier) is None:
            raise ValueError('short exact ASCII case identifier required')
        if identifier in identifiers:
            raise ValueError('case identifiers must be unique')
        identifiers.add(identifier)
        if any(type(x) is not int or not 0 <= x <= 10000 for x in (a, b)):
            raise ValueError('exact integer durations from0 through10000ms required')
        differences.append(a - b)
    return tuple(differences)


def paired_sign_flip(records):
    """Every independent fair within-pair sign assignment has weight1/2**n.

    This function does not establish that the experimental assignment or
    observational differences justify that conditional null model.
    """
    differences = checked_pairs(records)  # Validate the full data before work.
    n = len(differences)
    observed = sum(differences)
    histogram = {}
    # Each mask is an assignment, including equal results from ties/zeros.
    for mask in range(1 << n):
        signed_sum = sum(value if mask & (1 << i) else -value
                         for i, value in enumerate(differences))
        histogram[signed_sum] = histogram.get(signed_sum, 0) + 1
    assignments = 1 << n
    extreme = sum(weight for total, weight in histogram.items() if abs(total) >= abs(observed))
    less = sum(weight for total, weight in histogram.items() if total <= observed)
    greater = sum(weight for total, weight in histogram.items() if total >= observed)
    return {'differences_ms': differences, 'pair_count': n,
            'observed_sum_ms': observed, 'observed_mean_ms': Fraction(observed, n),
            'assignment_count': assignments, 'extreme_assignment_count': extreme,
            'p_two_sided': Fraction(extreme, assignments),
            'p_less': Fraction(less, assignments), 'p_greater': Fraction(greater, assignments),
            'null_sum_histogram_ms': tuple(sorted(histogram.items()))}


def weighted_sum_reference(differences):
    """Independent polynomial convolution: no sign masks or main function."""
    mass = {0: 1}
    for difference in differences:
        following = {}
        for previous, weight in mass.items():
            for increment in (-difference, difference):
                total = previous + increment
                following[total] = following.get(total, 0) + weight
        mass = following
    return tuple(sorted(mass.items()))


def records_for_differences(differences):
    """Trusted test fixtures only; nonnegative durations with chosen differences."""
    return tuple(('c' + str(i), max(value, 0), max(-value, 0))
                 for i, value in enumerate(differences))


def refuses(records):
    try:
        paired_sign_flip(records)
    except (ValueError, TypeError):
        return True
    return False


def main():
    result = paired_sign_flip(PAIRS_MS)
    checks = {}
    def check(label, condition):
        checks[label] = bool(condition)
        assert checks[label], label
    check('six_pairs_hand_mean5_over3_ms_and_exact_two_of64_tail',
          result['differences_ms'] == (2, 3, 1, 2, 1, 1)
          and result['observed_sum_ms'] == 10 and result['observed_mean_ms'] == Fraction(5, 3)
          and result['assignment_count'] == 64 and result['extreme_assignment_count'] == 2
          and result['p_two_sided'] == Fraction(1, 32))
    reference_profiles = 0
    for n in range(1, 6):
        for differences in product((-2, -1, 0, 1, 2), repeat=n):
            actual = paired_sign_flip(records_for_differences(differences))
            expected = weighted_sum_reference(differences)
            assert actual['null_sum_histogram_ms'] == expected
            tail = sum(weight for value, weight in expected if abs(value) >= abs(sum(differences)))
            assert actual['p_two_sided'] == Fraction(tail, 2 ** n)
            reference_profiles += 1
    check('independent_weighted_histogram_convolution_ONE_family', reference_profiles == 3905)
    tied = paired_sign_flip(records_for_differences((1, 1)))
    check('equal_statistic_values_keep_assignment_multiplicity',
          tied['null_sum_histogram_ms'] == ((-2, 1), (0, 2), (2, 1))
          and tied['p_two_sided'] == Fraction(1, 2)
          and Fraction(2, len(tied['null_sum_histogram_ms'])) == Fraction(2, 3))
    with_zero = paired_sign_flip(records_for_differences((1, 1, 0)))
    check('zero_difference_adds_label_assignments_without_changing_tail_probability',
          with_zero['assignment_count'] == 2 * tied['assignment_count']
          and with_zero['null_sum_histogram_ms'] == tuple((value, 2 * weight) for value, weight in tied['null_sum_histogram_ms'])
          and with_zero['p_two_sided'] == tied['p_two_sided'])
    zero = paired_sign_flip(records_for_differences((0, 0, 0)))
    check('all_zero_observed_effect_has_p_one_not_a_proven_null',
          zero['observed_mean_ms'] == 0 and zero['p_two_sided'] == 1 and zero['null_sum_histogram_ms'] == ((0, 8),))
    swapped = paired_sign_flip(tuple((identifier, b, a) for identifier, a, b in PAIRS_MS))
    check('swapping_all_version_labels_negates_effect_and_swaps_directional_tails',
          swapped['observed_mean_ms'] == -result['observed_mean_ms']
          and swapped['p_two_sided'] == result['p_two_sided']
          and swapped['p_less'] == result['p_greater'] and swapped['p_greater'] == result['p_less'])
    reordered = paired_sign_flip(tuple(reversed(PAIRS_MS)))
    check('reordering_whole_pairs_preserves_null_distribution',
          reordered['null_sum_histogram_ms'] == result['null_sum_histogram_ms']
          and reordered['observed_mean_ms'] == result['observed_mean_ms']
          and paired_sign_flip(PAIRS_MS[2:] + PAIRS_MS[:2])['null_sum_histogram_ms'] == result['null_sum_histogram_ms'])
    paired = paired_sign_flip((('x', 1, 2), ('y', 3, 4)))
    broken = paired_sign_flip((('x', 1, 4), ('y', 3, 2)))
    check('breaking_pairing_changes_p_even_when_both_marginal_samples_and_mean_stay_same',
          paired['observed_mean_ms'] == broken['observed_mean_ms'] == -1
          and paired['p_two_sided'] == Fraction(1, 2) and broken['p_two_sided'] == 1)
    shifted = tuple((identifier, a + 10 * i, b + 10 * i) for i, (identifier, a, b) in enumerate(PAIRS_MS))
    check('common_duration_offset_within_each_pair_cancels_algebraically',
          paired_sign_flip(shifted) == result)
    scaled = paired_sign_flip(tuple((identifier, 10 * a, 10 * b) for identifier, a, b in PAIRS_MS))
    check('physical_durations_scaled_in_same_ms_change_effect_not_p',
          scaled['observed_mean_ms'] == 10 * result['observed_mean_ms'] and scaled['p_two_sided'] == result['p_two_sided'])
    largest = paired_sign_flip(records_for_differences((10000,) * 12))
    smallest = paired_sign_flip((('only', 5, 0),))
    check('one_pair_and_twelve_pair_resource_endpoints_have_known_tail_counts',
          smallest['p_two_sided'] == 1 and largest['assignment_count'] == 4096
          and largest['extreme_assignment_count'] == 2 and largest['p_two_sided'] == Fraction(1, 2048))
    less_count = result['p_less'] * result['assignment_count']
    greater_count = result['p_greater'] * result['assignment_count']
    weight_at_observed = dict(result['null_sum_histogram_ms'])[result['observed_sum_ms']]
    check('inclusive_tails_keep_equal_values_and_two_sided_convention_is_explicit',
          less_count + greater_count == result['assignment_count'] + weight_at_observed
          and result['p_two_sided'] == min(Fraction(1), 2 * min(result['p_less'], result['p_greater']))
          and zero['p_two_sided'] == min(Fraction(1), 2 * min(zero['p_less'], zero['p_greater'])))
    class IntSubclass(int): pass
    class TupleSubclass(tuple): pass
    invalid = ((), [], (('x', 1),), (('x', True, 0),), (('x', 1.0, 0),),
               (('x', -1, 0),), (('x', 10001, 0),), (('x', IntSubclass(1), 0),),
               (('x', 1, 0), ('x', 2, 0)), (('', 1, 0),), (('x y', 1, 0),),
               ((1, 1, 0),), (TupleSubclass(('x', 1, 0)),), TupleSubclass((('x', 1, 0),)),
               tuple(('c' + str(i), 1, 0) for i in range(13)))
    check('full_record_shape_exact_types_identifiers_durations_and_resource_bounds_validated',
          all(refuses(value) for value in invalid))
    histogram = result['null_sum_histogram_ms']
    null_mean_sum = sum(total * weight for total, weight in histogram)
    null_second_sum = Fraction(sum(total * total * weight for total, weight in histogram), 64)
    check('conditional_null_mass_symmetry_mean_and_second_moment_match_fair_sign_model',
          sum(weight for _, weight in histogram) == 64
          and all(dict(histogram).get(-value) == weight for value, weight in histogram)
          and null_mean_sum == 0 and null_second_sum == sum(d * d for d in result['differences_ms']) == 20)
    assert all(type(value) is bool and value for value in checks.values())
    return {'python': platform.python_version(), 'dependencies': 'standard library only',
            'synthetic_paired_durations_ms': PAIRS_MS, 'result': result,
            'null_model': 'Conditional independent fair within-pair signs, not inferred from input.',
            'tail_rule': 'Count all2**n assignments with abs(sum)>=abs(observed sum); exact inclusive absolute tail.',
            'p_is_not_effect_size_or_posterior_null_probability': True,
            'multiplicity_witness': tied, 'zero_difference_witness': with_zero,
            'broken_pairing_same_mean_witness': {'paired': paired, 'broken': broken},
            'tenfold_physical_duration_scaling_same_ms': {'mean_ms': scaled['observed_mean_ms'], 'p': scaled['p_two_sided']},
            'conditional_null_sum_variance_ms_squared': null_second_sum,
            'reference_profiles_in_ONE_family': reference_profiles,
            'actual_service_requests': 0, 'actual_timer_measurements': 0,
            'random_draws': 0, 'scipy_executed': False, 'checks': checks,
            'unique_check_groups': len(checks),
            'limits': 'No assignment, independence, carryover or matching guarantee from numerical code. No actual benchmark, trial, population effect interval, P95 improvement or causal product claim.'}


def fraction_string(value):
    if type(value) is Fraction:
        return str(value)
    raise TypeError('unsupported output value')


if __name__ == '__main__':
    print(json.dumps(main(), ensure_ascii=False, indent=2, default=fraction_string))

مسیرِ اصلی همهٔ ماسک‌های علامت را می‌سازد. مرجعِ مستقل از آن استفاده نمی‌کند: توزیع را با افزودنِ پیاپیِ دو افزایشِ مثبت و منفی، همراهِ وزنِ حالت‌های قبلی، می‌سازد. این هم‌پیچیِ گسسته، تکرارها را نگه می‌دارد. مقایسهٔ ۳۹۰۵ پروفایلِ کوچک یک خانوادهٔ بررسی است، نه هزاران آزمایشِ سرویس یا اجرای مستقلِ مقاله.

چهارده گروهِ نام‌دار، مرجعِ دستی، مرجعِ وزن‌دار، صفر و تکرار، حفظِ جفت، جابه‌جاییِ برچسب، مرزِ تعداد، ورودیِ نامعتبر و گشتاورهای مدل را می‌سنجند. ده‌برابرکردنِ همهٔ مدت‌ها با حفظِ واحدِ میلی‌ثانیه، اثر را ده‌برابر می‌کند ولی p را تغییر نمی‌دهد. این شاهد نشان می‌دهد احتمالِ دنباله جایِ گزارشِ اندازهٔ اثر را نمی‌گیرد؛ عددِ کوچک‌ترِ p به‌تنهایی فایدهٔ عملی را رتبه‌بندی نمی‌کند.

{
  "python": "3.14.5",
  "dependencies": "standard library only",
  "synthetic_paired_durations_ms": [
    [
      "case-a",
      12,
      10
    ],
    [
      "case-b",
      15,
      12
    ],
    [
      "case-c",
      14,
      13
    ],
    [
      "case-d",
      11,
      9
    ],
    [
      "case-e",
      16,
      15
    ],
    [
      "case-f",
      13,
      12
    ]
  ],
  "result": {
    "differences_ms": [
      2,
      3,
      1,
      2,
      1,
      1
    ],
    "pair_count": 6,
    "observed_sum_ms": 10,
    "observed_mean_ms": "5/3",
    "assignment_count": 64,
    "extreme_assignment_count": 2,
    "p_two_sided": "1/32",
    "p_less": "1",
    "p_greater": "1/64",
    "null_sum_histogram_ms": [
      [
        -10,
        1
      ],
      [
        -8,
        3
      ],
      [
        -6,
        5
      ],
      [
        -4,
        8
      ],
      [
        -2,
        10
      ],
      [
        0,
        10
      ],
      [
        2,
        10
      ],
      [
        4,
        8
      ],
      [
        6,
        5
      ],
      [
        8,
        3
      ],
      [
        10,
        1
      ]
    ]
  },
  "null_model": "Conditional independent fair within-pair signs, not inferred from input.",
  "tail_rule": "Count all2**n assignments with abs(sum)>=abs(observed sum); exact inclusive absolute tail.",
  "p_is_not_effect_size_or_posterior_null_probability": true,
  "multiplicity_witness": {
    "differences_ms": [
      1,
      1
    ],
    "pair_count": 2,
    "observed_sum_ms": 2,
    "observed_mean_ms": "1",
    "assignment_count": 4,
    "extreme_assignment_count": 2,
    "p_two_sided": "1/2",
    "p_less": "1",
    "p_greater": "1/4",
    "null_sum_histogram_ms": [
      [
        -2,
        1
      ],
      [
        0,
        2
      ],
      [
        2,
        1
      ]
    ]
  },
  "zero_difference_witness": {
    "differences_ms": [
      1,
      1,
      0
    ],
    "pair_count": 3,
    "observed_sum_ms": 2,
    "observed_mean_ms": "2/3",
    "assignment_count": 8,
    "extreme_assignment_count": 4,
    "p_two_sided": "1/2",
    "p_less": "1",
    "p_greater": "1/4",
    "null_sum_histogram_ms": [
      [
        -2,
        2
      ],
      [
        0,
        4
      ],
      [
        2,
        2
      ]
    ]
  },
  "broken_pairing_same_mean_witness": {
    "paired": {
      "differences_ms": [
        -1,
        -1
      ],
      "pair_count": 2,
      "observed_sum_ms": -2,
      "observed_mean_ms": "-1",
      "assignment_count": 4,
      "extreme_assignment_count": 2,
      "p_two_sided": "1/2",
      "p_less": "1/4",
      "p_greater": "1",
      "null_sum_histogram_ms": [
        [
          -2,
          1
        ],
        [
          0,
          2
        ],
        [
          2,
          1
        ]
      ]
    },
    "broken": {
      "differences_ms": [
        -3,
        1
      ],
      "pair_count": 2,
      "observed_sum_ms": -2,
      "observed_mean_ms": "-1",
      "assignment_count": 4,
      "extreme_assignment_count": 4,
      "p_two_sided": "1",
      "p_less": "1/2",
      "p_greater": "3/4",
      "null_sum_histogram_ms": [
        [
          -4,
          1
        ],
        [
          -2,
          1
        ],
        [
          2,
          1
        ],
        [
          4,
          1
        ]
      ]
    }
  },
  "tenfold_physical_duration_scaling_same_ms": {
    "mean_ms": "50/3",
    "p": "1/32"
  },
  "conditional_null_sum_variance_ms_squared": "20",
  "reference_profiles_in_ONE_family": 3905,
  "actual_service_requests": 0,
  "actual_timer_measurements": 0,
  "random_draws": 0,
  "scipy_executed": false,
  "checks": {
    "six_pairs_hand_mean5_over3_ms_and_exact_two_of64_tail": true,
    "independent_weighted_histogram_convolution_ONE_family": true,
    "equal_statistic_values_keep_assignment_multiplicity": true,
    "zero_difference_adds_label_assignments_without_changing_tail_probability": true,
    "all_zero_observed_effect_has_p_one_not_a_proven_null": true,
    "swapping_all_version_labels_negates_effect_and_swaps_directional_tails": true,
    "reordering_whole_pairs_preserves_null_distribution": true,
    "breaking_pairing_changes_p_even_when_both_marginal_samples_and_mean_stay_same": true,
    "common_duration_offset_within_each_pair_cancels_algebraically": true,
    "physical_durations_scaled_in_same_ms_change_effect_not_p": true,
    "one_pair_and_twelve_pair_resource_endpoints_have_known_tail_counts": true,
    "inclusive_tails_keep_equal_values_and_two_sided_convention_is_explicit": true,
    "full_record_shape_exact_types_identifiers_durations_and_resource_bounds_validated": true,
    "conditional_null_mass_symmetry_mean_and_second_moment_match_fair_sign_model": true
  },
  "unique_check_groups": 14,
  "limits": "No assignment, independence, carryover or matching guarantee from numerical code. No actual benchmark, trial, population effect interval, P95 improvement or causal product claim."
}

اجرای واقعی Python 3.14.5 است. مستندِ SciPy خوانده‌شده نسخهٔ 1.18.0 دارد؛ دو صفحهٔ NIST تاریخِ انتشارِ اولیهٔ معلومی ندارند و تاریخِ دسترسیِ ۲۸ مهر ۱۴۰۵ را جایِ آن نمی‌گذاریم. درسِ تکرارپذیریِ آزمایش به ثبتِ ورودی و پیکربندی می‌پردازد؛ بازسازیِ دقیقِ این حساب هم جایِ طراحیِ معتبرِ آزمایش را نمی‌گیرد.

۵. چهار پرسش دربارهٔ آزمایش و تفسیر

نتیجهٔ عددی، طرحِ آزمایش و فایدهٔ عملی را تضمین نمی‌کند
تصویر مفهومی: نتیجهٔ عددی، طرحِ آزمایش و فایدهٔ عملی را تضمین نمی‌کند؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

پرسش اول: هر شش مقدارِ B کمترند؛ چرا فقط نتیجه نگیریم نسخه بهتر است؟ پاسخ: این‌ها دادهٔ تألیفی‌اند. در کاربردِ واقعی، حتی جهتِ مشترکِ اختلاف‌ها می‌تواند با ترتیب، کش یا بارِ مشترک همراه باشد. فرضِ تصادفی‌سازی و معیارِ فایده باید بیرون از تابع توجیه شوند. تابع نه تخصیص را دیده و نه آزمایشِ مستقلی روی سرویس انجام داده است.

پرسش دوم: چرا جابه‌جاکردنِ جفت‌ها مهم است وقتی میانگین ثابت می‌ماند؟ پاسخ: برای A برابر یک و سه و B برابر دو و چهار، دو اختلافِ منفیِ یک داریم و p یک‌دوم است. اگر فقط B را وارونه کنیم، اختلاف‌ها منفیِ سه و یک می‌شوند و p یک است؛ میانگین هنوز منفیِ یک است. جفت‌بندی باید از طرح بیاید، نه با انتخابِ نتیجهٔ خوشایند تغییر کند.

پرسش سوم: اگر همهٔ اختلاف‌ها صفر و p یک شد، برابری ثابت می‌شود؟ پاسخ: خیر؛ این آزمون شاهدی از این آماره علیهِ مدل نمی‌سازد. دقتِ ثبت، تعدادِ کم و اثرهای ندیده باقی‌اند. حتی یک جفتِ دارای اختلافِ غیرصفر در آزمونِ دوطرفهٔ حاضر p یک می‌دهد. از نبودِ شاهد، تضمینِ برابری یا بازهٔ اثر استخراج نمی‌کنیم.

پرسش چهارم: آیا p کمتر از پنج‌صدم مجوزِ انتشارِ نسخه است؟ پاسخ: خیر؛ آستانه، آماره، جهتِ مقایسه و طرحِ توقف باید پیشاپیش تعریف شوند و آزمون‌های متعدد نیز مسئلهٔ جدا دارند. اثرِ میلی‌ثانیه‌ای، دنبالهٔ تأخیر، خطاها و هزینهٔ تغییر را کنار هم بررسی کنید. این درس فقط حسابِ دقیقِ یک مدلِ شرطی را تأیید می‌کند، نه تصمیمِ استقرار یا بهبودِ اندازه‌گیری‌شدهٔ محصول.

منابع و مطالعهٔ بیشتر

بررسی منابع: ۲۸ مهر ۱۴۰۵. تحلیل‌ها و پیشنهادهای اجرایی، جمع‌بندی نویسنده‌اند.