К обычному разбору
Тренировка по собеседованиюСозвон после собеседованияStrala2025-09-24

Strala: Алгоритмическая секция

Идите сверху вниз: сначала попробуйте сами, затем откройте разбор. Если шаг с кодом, пишите решение прямо здесь и запускайте проверки на странице.

Шагов
3
Вопросов
2
Задач
1
1ЗадачаMedium

Отрисовка пиксельного экрана в терминале

Условие

Build a screen representation with colored pixels, update operations, arbitrary line drawing and triangle edge drawing. Return deterministic symbolic pixels for the trainer version.

Решение прямо на странице

Напишите код, запустите проверки и только потом открывайте разбор.

Проверка решения

Нажмите «Запустить проверки» или Ctrl+Enter.

Показать разбор

Подсказки

  • Разделите модель и вывод

    Храните цвета как значения в сетке, а в символы превращайте только на финальном выводе.

  • Главная сложность — линия

    Линия должна работать для крутых наклонов и обратного порядка концов, а не только для пологих диагоналей слева направо.

Идея решения

Удобно хранить экран как двумерный список символов цветов. Команда set обновляет одну ячейку. Команда triangle — это три команды рисования линии.

Для произвольных наклонов линии используйте алгоритм Брезенхэма или эквивалентный целочисленный шаговый алгоритм. Важная часть — одинаково корректно обработать пологие, крутые, горизонтальные, вертикальные и обратные линии, рисуя оба конца.

На исходном собеседовании использовались ANSI-цвета терминала. Версия для тренажёра сохраняет идею экрана, пикселей и линий, но рендерит в детерминированные символы, чтобы задачу можно было проверять в браузере.

Эталонный код

def render_pixel_screen(height: int, width: int, commands: list[dict]) -> list[str]:
    palette = {"black": ".", "blue": "B", "yellow": "Y"}
    screen = [[palette["black"] for _ in range(width)] for _ in range(height)]

    def set_pixel(row: int, col: int, color: str) -> None:
        if 0 <= row < height and 0 <= col < width:
            screen[row][col] = palette[color]

    def draw_line(start, end, color: str) -> None:
        r1, c1 = start
        r2, c2 = end
        dr = abs(r2 - r1)
        dc = abs(c2 - c1)
        sr = 1 if r1 < r2 else -1
        sc = 1 if c1 < c2 else -1
        err = dc - dr

        row, col = r1, c1
        while True:
            set_pixel(row, col, color)
            if row == r2 and col == c2:
                break
            twice_err = 2 * err
            if twice_err > -dr:
                err -= dr
                col += sc
            if twice_err < dc:
                err += dc
                row += sr

    for command in commands:
        kind = command["type"]
        color = command["color"]
        if kind == "set":
            set_pixel(command["row"], command["col"], color)
        elif kind == "line":
            draw_line(command["start"], command["end"], color)
        elif kind == "triangle":
            p1, p2, p3 = command["points"]
            draw_line(p1, p2, color)
            draw_line(p2, p3, color)
            draw_line(p3, p1, color)
        else:
            raise ValueError(f"unknown command type: {kind}")

    return ["".join(row) for row in screen]
Сложность
Время: O(h * w + p). Память: O(h * w).
Сетка инициализируется один раз. p — суммарное число пикселей, которые рисуются всеми командами.
2Вопрос8 мин

Вопрос про production ML

Multiple threads update individual pixels of the same screen. What can go wrong, and how would you design synchronization?

Ответьте без подсказки

Сначала проговорите ответ вслух или тезисами.

Запишите черновик

Формулы, план решения, риски и примеры.

Сравните с разбором

Откройте разбор только после своей попытки.

Показать разбор

Короткий ответ

The main risks are race conditions, lost updates and inconsistent intermediate state. Use a single writer, a queue, per-region locks or atomic updates depending on ordering and throughput needs.

Подробный разбор

If several threads mutate the same screen array, two updates can interleave unpredictably. A pixel may end in the wrong state, a reader can observe a partially updated frame, and coarse rendering operations such as a line can be torn across frames.

The simplest robust design is a single writer: worker threads submit update commands to a queue, and one render loop applies them in order. This gives deterministic ordering and avoids fine-grained locks. If throughput requires parallel writes, use lock striping by row/tile, per-pixel atomics for very small writes, double buffering, or transactional frame batches.

Lock granularity is a tradeoff. A global screen lock is easy but serializes everything. Per-pixel locks are expensive and can deadlock if multi-pixel operations acquire several locks. Tiled locks or a command queue are often a better engineering compromise.

Типичные ошибки

  • Use one global lock without discussing throughput.
  • Use per-pixel locks for line/triangle updates without a lock ordering rule.
  • Forget that readers can observe partially drawn frames.

Как сказать на собеседовании

  • State whether updates must be ordered exactly.
  • Offer a simple single-writer design before optimizing.
3Вопрос8 мин

Вопрос про production ML

Multiple clients send pixel updates to a central server over the internet. What transport/protocol would you use and what tradeoffs matter?

Ответьте без подсказки

Сначала проговорите ответ вслух или тезисами.

Запишите черновик

Формулы, план решения, риски и примеры.

Сравните с разбором

Откройте разбор только после своей попытки.

Показать разбор

Короткий ответ

For reliable ordered UI state, WebSocket over TCP is the default. For lossy high-rate rendering, UDP/WebRTC-style transport can work if updates are idempotent or sequence-numbered.

Подробный разбор

For a browser or app that sends small pixel-update messages to a central server, a WebSocket over TCP is a practical default. It gives a persistent bidirectional connection, reliable delivery and in-order messages. The payload can be compact JSON at low volume or a binary format when update rate matters.

The tradeoff is latency under packet loss. TCP preserves order, so a lost packet can block later updates. If the screen is a real-time stream where stale updates can be dropped, UDP or WebRTC data channels may be better, but then the application needs sequence numbers, idempotent commands, snapshots or reconciliation.

The server should also define conflict semantics: last-write-wins by server timestamp, client sequence numbers, per-client authority or command batching. Transport alone does not solve state consistency.

Типичные ошибки

  • Say TCP without explaining ordering and head-of-line blocking.
  • Say UDP without explaining loss, reordering and recovery.
  • Ignore conflict resolution when several clients update the same pixel.

Как сказать на собеседовании

  • Start with WebSocket for product simplicity.
  • Mention binary payloads only after correctness semantics are clear.