Skip to content

feat(report): сравнение с точным оптимумом очереди в каждом прогоне - #10

Merged
retrobich921 merged 1 commit into
mainfrom
feat/optimality-in-report
Sep 6, 2026
Merged

retrobich921 merged 1 commit into
mainfrom
feat/optimality-in-report

Conversation

@retrobich921

Copy link
Copy Markdown
Contributor

Что и зачем

Эталон min-cost flow (Analytics::AssignmentBound) существовал с самого начала, но был доступен только командой bound и никуда не попадал — ни в отчёт, ни в сводку. Организаторы на чекпоинте 3 просили одну особенность, поданную ближе к концу презентации. Производительность на эту роль не годится: очередь будет маленькая («там не очень большое будет», 08:24), и 10 600 заявок/с жюри не заметит.

Вместо неё показываем то, чего нет ни у кого: роутер знает, насколько он близок к математическому пределу.

Как

  • Runner#route считает AssignmentBound на своих же решениях и кладёт в отчёт блоком optimality. Runner#bound теперь достаёт готовый результат вместо повторного прогона роутинга.
  • Видно в трёх местах: optimality в routing_report.json, карточка в HTML-дашборде, строка в сводке CLI.
  • gain_matrix больше не ищет заявку линейным поиском на каждую пару «банк × провайдер» — иначе расчёт в основном прогоне стоил бы O(банки × провайдеры × заявки).

Что было неправильно в первой версии

Считал долю от quota_optimum — «оптимума при тех же целевых долях». На очереди в 200 заявок роутер обошёл его (147.74 против 141.77), и клэмп прятал это за «100%». Причина: доли у роутера мягкая цель, а у эталона были жёстким ограничением — он решал более стеснённую задачу.

Знаменателем стал free_optimum: наше распределение допустимо в его задаче, поэтому это настоящая верхняя граница и превысить её нельзя по построению. Цифры честнее и скромнее — 83.8% на публичной очереди, 93.6% на 200 заявках, 96.4% на 1000. Регресс-тест с зафиксированными числами держит этот случай.

Отставание раскладывается без остатка на два слагаемых: на публичной очереди из 1.26 недобранного одобрения 1.09 — цена обязательства держать целевые доли и только 0.18 — цена онлайн-решений. Пять шестых стоит бизнес-требование, а не качество роутинга.

Заодно

Исправлена атрибуция в docs/checkpoint.md: «дневной лимит считаем только по выполненным заявкам» было нашей репликой на чекпоинте, а не вердиктом экспертов. Вопрос 1 возвращён в открытые — сдаём формулу ТЗ под свою ответственность. Туда же записаны реальные ответы чекпоинта 3: провайдеры к тестовой очереди не меняются, сдача через GitHub, презентация свободного формата.

Как тестировать

bundle exec rake            # 254 спека, rubocop
bundle exec rake public     # артефакты в корне + валидатор организаторов
bundle exec rake stress     # 17 сценариев
ruby -Ilib bin/payout_router bound --synthetic 200

254 спека зелёные (покрытие 96.2%), rubocop чист, стресс 17 сценариев без нарушений инвариантов, валидатор организаторов 0 ошибок. routing_decisions.json не изменился — формат сдачи не тронут.

🤖 Generated with Claude Code

https://claude.ai/code/session_018V8U5HbXjW86D7cgz88m5G

Эталон min-cost flow был доступен только командой bound и никуда не попадал.
Организаторы на чекпоинте 3 просили одну особенность, поданную ближе к концу
презентации; производительность на эту роль не годится — очередь будет маленькая
(«там не очень большое будет», 08:24). Вместо неё показываем то, чего нет ни у кого:
роутер знает, насколько он близок к математическому пределу.

- Runner#route считает AssignmentBound на своих же решениях и кладёт в отчёт
  блоком optimality; Runner#bound теперь достаёт готовый результат вместо
  повторного прогона роутинга.
- Главная цифра score_vs_optimum считается от free_optimum — единственной
  настоящей верхней границы (наше распределение допустимо в его задаче).
  От quota_optimum её считать нельзя: доли у роутера мягкая цель, и на очереди
  в 200 заявок он его законно обходит (147.74 против 141.77), а клэмп прятал
  это за «100%». Регресс-тест с зафиксированными числами.
- Отставание раскладывается без остатка на цену целевых долей и цену онлайн-решений:
  на публичной очереди 1.09 и 0.18 из 1.26. Пять шестых стоит бизнес-требование,
  а не качество роутинга.
- Видно в трёх местах: optimality в routing_report.json, карточка в HTML,
  строка в сводке CLI.
- gain_matrix больше не ищет заявку линейным поиском на каждую пару «банк ×
  провайдер» — иначе расчёт в основном прогоне стоил бы O(банки × провайдеры × заявки).
- Доки: pitch.md раздел 8 под фишку, README, CLAUDE.md, разбор ответов чекпоинта 3.

Отдельно исправлена атрибуция в docs/checkpoint.md: «дневной лимит считаем только
по выполненным заявкам» было нашей репликой на чекпоинте, а не вердиктом экспертов.
Вопрос 1 возвращён в открытые — сдаём формулу ТЗ под свою ответственность.

254 спека зелёные, rubocop чист, стресс 17 сценариев без нарушений,
валидатор организаторов 0 ошибок.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018V8U5HbXjW86D7cgz88m5G
@retrobich921
retrobich921 merged commit b10efb8 into main Sep 6, 2026
1 check passed
@retrobich921
retrobich921 deleted the feat/optimality-in-report branch September 6, 2026 13:34
verikirill added a commit that referenced this pull request Sep 6, 2026
…й копии в Docker

Фичу из #10 показываем отдельным слайдом ближе к концу деки — организаторы на чекпоинте 3
просили одну особенность именно там. Числа проверены своим прогоном: 83.8% от точного
оптимума на публичной очереди, 96.4% на тысяче заявок, отставание разложено на цену
обязательства держать доли (1.09) и цену онлайн-режима (0.18).

Все счётчики приведены к состоянию после мержа: 254 спека, покрытие строк 96.2%.
Перепроверено на чистой копии в Docker ruby:4.0 — спеки и rubocop зелёные, скрипт
организаторов 29/0/0, наш валидатор 30/0/0, stress 17 сценариев без нарушений
инвариантов, bench 9 942 заявки/с на 50 тыс. и 9 399 на 200 тыс.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant