Превращение морозильника в холодильник: юный инженер и простой адаптер | 2025-12-19T00:56:58

Сегодня продал холодильник. У него есть история. Заключается она в том, что это не холодильник, хоть и выглядит как холодильник. Это морозильник. И морозит он в среднем до минус 18. А я его с рук покупал, и думал, что это холодильник. Покупатель сегодня тоже кстати приехала, думая, что это холодильник.

И вот я понимаю, что минус 18 мне совсем не надо.

Ну что, я ж Solution Architect. Лезть внутрь я не хотел, я просто поехал в Lowe’s и купил простенькую мигалку. Она по расписанию включает и выключает то, что в нее воткнуто. Сунул внутрь радиотермометр (у меня был), и подобрал частоту «мигания» (20 минут), чтобы внутренняя температура была в среднем +4 градусов. Радиотермометр показал, что флуктуации температуры совсем маленькие — условно плюс-минус 0.5 градуса от +4, даже меньше. И так он у меня работал сколько-то там месяцев, пока я понял, что он мне просто не нужен.

Сегодня продал с адаптером. Ушло в народ.

Нефтегазовые «ёлки»: больше, чем просто украшение | 2025-12-18T18:34:08

О, сколько нам открытий чудных приносит просвященья дух…

оказывается, Christmas tree в oil & gas industry — это фонтанная арматура. Это я по работе поиск тестирую

Перевод Excel-организма в код: стратегия и исполнение | 2025-12-17T18:56:17

Все мы с этим сталкивались — «Главная Excel-Таблица, Управляющая Бизнесом». Та самая, которую B2B-компании используют, чтобы считать котировки на миллионы долларов. В ней 12 вкладок, 1000+ вложенных формул и ноль документации. Десять лет туда лепили «быстрые фиксы» и прятали константы. Это уже не файл, а живой организм, который уже никто до конца не понимает кроме того чела, уволившегося годы назад. Вот такой я был озадачен. Более того, там еще была неопределенность нужна ли вообще половина формул, или это рудименты прошлого.

Типичная ячейка:

=IF($D11=$D10,»», IF(ISNUMBER( INDEX(Data!$T$10:$U$17,

MATCH(TabCalc!$F11,Data!$T$10:$T$17,0),2)),

INDEX(Data!$T$10:$U$17, MATCH(TabCalc!$F11,Data!$T$10:$T$17,0),2),

INDEX(TabProd!$C$8:$U$112,TabCalc!$D11,I$1)))

Мне поручили перенести эту логику в код, чтобы все считалось софтом. Excel-файл как бы все имел что надо, но по факту — это был сложнораспутываемый черный ящик. 1069 формул.

Челлендж был в том, как перевести тысячу взаимозависимых формул в чистый код и не потерять ни одного пограничного случая (edge case).

В итоге вот что я сделал.

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

Я построил структуру на Groovy, которая имитировала поведение Экселя. Каждое вычисление (из ячейки) я определил как функцию, которая выполняется только тогда, когда её вызывают. А функциями был многомерный dictionary.

Я пошел с конца графа вычислений: от результатов к входным данным. Если формула зависела от чего-то, что я еще не написал, я «мокал» это в коде, просто подставляя значение из Excel-листа.

Кусок за куском я заменял эти моки на реальную логику. Сравнивая выхлоп моего кода с экселькой на каждом шаге, я точно видел, где моя логика расходится.

Другими словами, движение шло от результата к исходным данным. На каждом шаге было ясно, какие моки надо превратить в код, и можно было сравнить версию +1 с версией -1 — результат должен был совпадать. Как только все моки заменились на вызовы — задача была готова.

Настоящим «секретным ингредиентом» стала динамическая природа Groovy для создания многомерной карты функций. Вместо статических переменных я использовал глубоко вложенную структуру, где каждый «лист» был замыканием (closure). Это позволило обращаться к любой части таблицы — будь то входной параметр, константа конфига или сложный промежуточный результат — через простой, унифицированный синтаксис, причем некоторые компоненты были динамическими.

Вот пример:

conf[«group»] = { x -> [«a», «b», «c»] }

conf[«group»]().each {

calculate[«Group»][«Subgroup»][it][«TotalQuantity»] =

{

x -> calculate[«Group»][«Subgroup»][it][«Someparameter»]() * conf[«someConstant»]()

}

}

Используя динамические ключи и замыкания, я мог итерироваться по группам продуктов или наборам данных. Поскольку это были динамические функции, а не сохраненные значения, вся система работала как живой граф зависимостей.

Тестировать можно было прямо сразу после начала переноса формул. Прелесть была в том, что ты вроде как как бы к ячейке обращаешься через синтаксис типа calculate[«Totals»][«A»](), а на самом деле запускаешь целое дерево вычислений в этот момент. И это дико удобно в отладке.

Через две недели «Черный ящик» превратился в прозрачную, модульную библиотеку с понятной логикой, которая выдавала ровно тот же результат, что и оригинальная таблица.

P.S. Ну и конечно, все данные на всех скриншотах тщательно обфусцированы, а точнее сказать, написаны с нуля для этого текста.

Разгадывая лабиринт запросов: как я превратил хаос в четкий код | 2025-12-17T03:25:38

Ух какую я прикольную задачку только что решил. Хрен только объяснишь. Ну я попробую.

Короче, у клиента есть 10 сайтов с поиском. Они все используют один индекс, но кидают разные запросы к нему. К тому, что вводит пользователь, прибавляется очень длинный и сложный query, который генерирует модуль на сайткоре. Он содержит айдишники шаблонов и страниц, которые нужно включать или исключать. В итоге понять, что там происходит, вообще невозможно. Там может быть десять открывающих скобок и где-то рандомно закрывающие, но с Coveo работало. Реформаттинг помогал, но не сильно.

И у каждого сайта такое свое. При этом там фигурируют периодически одни и те же айдишники. Я сначала пытался в этом вручную разобраться, но это был кошмар. Ни фига не помогает. Там же еще вложенные условия. Например, «исключить этот шаблон» не глобально, а только если вон то поле равно единице.

В итоге вот что я сделал:

Написал скрипт, который разбирает эту текстовую «кашу» в абстрактное синтаксическое дерево (AST). Это позволило превратить нечитаемую строку в структурированный JSON-объект, где четко видно: вот тут AND, тут OR, а тут — конкретное условие.

Дальше я превратил эти условия в формулы булевой алгебры. С помощью библиотеки SymPy я «скормил» эти формулы алгоритмам упрощения. Математика сама выкинула дубликаты, схлопнула лишние вложенности и убрала условия, которые логически поглощаются другими. В результате «деревья» стали плоскими и понятными.

В аттаче — оригинальное дерево и упрощенное.

Чтобы быть уверенным, что я ничего не сломал при упрощении, я написал генератор тестов. Он берет упрощенную логику, собирает её обратно в рабочий curl и проверяет, совпадает ли количество найденных документов (totalCount) с оригинальным запросом. Цифры сошлись — значит, логика сохранена на 100%.

Имея на руках упрощенные и стандартизированные структуры для каждого сайта, я построил матрицу сравнения. Скрипт проанализировал их и выделил Common Core — условия, которые гарантированно требуются (или запрещены) на всех сайтах без исключения, и Specifics — уникальные «хвосты», которые отличают один сайт от другого.

На приложенном скриншоте: REQ означает, что условие гарантированно выполняется для любого документа, который пройдет через этот запрос. NOT — гарантированно не выполняется. OPT — условие присутствует в запросе, но оно не является строгим само по себе. Оно работает только в связке с чем-то еще. «.» — условие вообще не упоминается в запросе.

Для 3 сайтов моментально отвечает, для 10 работает минут 30.

Ну и конечно, все данные на всех скриншотах тщательно обфусцированы.

Каждое дерево на карте: Открытые данные графства | 2025-12-15T15:40:16

Смотрю какие у нас в каунти есть открытые данные для поиграться с анализом данных на выходных, и обнаружил, что есть, например, открытая база данных всех 1.5 млн деревьев графства. На скриншоте очень маленькая часть вокруг моего дома

От идеи к игровому AI: разработка шахматного алгоритма | 2025-12-15T04:33:13

Пока разбирался с нейросетями, решил придумать себе игровую задачку. А что если я найду где-то готовые партии, и обучу нейросетку предсказывать ход по ситуации на доске. Сказано — сделано. Конечно, код быстрее генерить с помощью LLM, но задание подробное писал сам и архитектуру придумывал сам. Через 40 минут (!) от идеи до результата: у меня уже было работающее решение, которое ну по крайней мере в первой половине партии не очень сильно косячит.

На скриншоте CuteChess — он работает с любым шахматным движком, и в моем случае это простой скрипт на питоне. Скрипт берет ситуацию на доске и скармливает ее модели. Выбирает топ 5 ходов, и только эти топ 5 просматривает вглубь на несколько ходов вперед и оценивает позицию. То есть, нейросеть у меня предлагает возможные ходы на основе анализа 20000 партий (534453 позиций). Из того, что получается, выбирается лучшее. Там используется для этого алгоритм minimax, если это кому-то что-то говорит (мне не очень говорило, поэтому Gemini тут мне помог)

Как тренируется модель. На сайте lichess можно скачать партии, там сотни гигабайт. Я взял файлик с 800000 сыгранными партиями за 2014 год. Из этих 800000 я отбираю 20000, а именно скриптом ищу партии, где результат не ничья (1-0 или 0-1). Далее считаю разницу (Рейтинг_Победителя минус Рейтинг_Проигравшего). Это не самая лучшая метрика, но лучше, чем ничего. Чем больше эта разница, тем «увереннее» должен быть выигрыш (сильный наказывает слабого). Итого получается 20000 таких партий.

«Игнорирование ходов слабого» (чтобы модель не учить плохому) реализуется на этапе тренировки модели. Фактически, логика такая: «Если сейчас ход белых, и белые выиграли эту партию — учимся. Если сейчас ход черных, и черные проиграли — пропускаем и не учим сеть этому ходу» .

Нейросеть тренируется батчами по 128 позиций за раз. Сеть получает на вход позицию на доске и выдает 4096 — оценку вероятности для каждого возможного хода.

Отбор партий занимает минут 5. Тренировка модели у меня на компе занимает минут 10 для 20000 игр. Можно оставить как-нибудь потренироваться на 100К или на миллионе, будет точно лучше. Только уже не надо — я разобрался 🙂

Партию можно посмотреть тут:

Магия искусственных нейронных сетей: разделение гласных и согласных | 2025-12-14T23:35:00

Сейчас экспериментирую с обучением простых нейросетей — главным образом для доведение до автоматизма имеющегося инструментария и некоторые штуки просто создают впечатление магии.

Есть база из 32000 имен. Есть нейросеть, заполеннная случайными числами. Запускаю тренировку, на входе — только этот список имен. Первый слой нейросети — эмбеддинги, и я выставляю число измерений 2, чтобы было легко визуализировать. И после 200000 итераций обучения система четко разделяет гласные от согласных, и почему-то чуть в стороне от других согласных ставит букву «q». Похоже, это потому, что буква ‘q’ почти исключительно предсказывает букву ‘u’ (Queen, Quincy, Quentin).

На русских именах тоже очень надежно разделяет гласные и согласные. В русских именах буквы б и л почему-то поодаль от остальных согласных, как и мягкий и твердый знаки (ну с ними понятно).

Интересно, как ж оно работает. Если на нормальном корпусе текстов натренировать, разница будет совсем четкая. Почему отделяются гласные от согласных? Видимо, с точки зрения математики сети, ‘а’ и ‘о’ выполняют одинаковую функцию: они «триггерят» предсказание согласной, следующей за ними, то есть виной всему чередование гласных и согласных. Но черт побери интересно 🙂

Ну и поскольку моделька умеет предсказывать следующие буквы, можно попробовать запустить ее на русском. На модели с эмбеддингами 30 измерений вот такие имена придумывает: бякетта, афсена, еракей, засбат, дарая, гайомахад, раин, ражул, гжаций, ребен, вуреб, дуродира, туружул, регравгава, разсан, габила, авганжа, рахси, халебкохорта, ратхер. Модель — для тех, кто разбирается — такая: вход 6х33 символа (потому что берем до 6 символов контекста), закодированных эмбеддингами в 60, идут на слой в 100 нейронов, а с них обратно на 33 символа. Фигня какая-то, но по крайней мере понятно как это все работает на всех уровнях.

Читаем больше: Как изменилась структура потребления информации с 1980 по 2008 год | 2025-12-14T22:33:27

Интересное исследование попалось на глаза, аж 2009 года. Согласно ему, современный человек действительно читает значительно больше, чем в прошлом, хотя формат этого чтения изменился. Согласно ему, на 2008 год, среднестатистический амеканец потребляет около 100000 слов в день (примерно чертверть «Войны и мира») — это приблизительное количество слов, которые прошли через сознание за день (через уши или глаза), рассчитанное на основе хронометража активности. Это на 140% больше, чем в 1980 году.

Таким образом, вопреки мифу о деградации чтения, как минимум в 2008 мы обрабатывали в 2.4 раза больше текстовой информации, чем поколение наших родителей. Причем исследование учитывало только информацию, потребляемую вне работы (дома, в пути, на отдыхе) .

Структура чтения — если в 1960 году 26% слов приходило с бумаги, то к 2008 году эта доля упала до 9%. Однако цифровые носители (интернет, электронная почта, соцсети) не только компенсировали этот спад, но и утроили общее время чтения. Причина — интернет, так как это преимущественно текстовая среда (веб-серфинг, email)

Но интересно, что Интернет обеспечивает 25% потребляемых слов, но лишь 2% байтов (так как видео в интернете в 2008 году было низкого качества). То есть, они там прикинули информационный поток с разных каналов и перевели его в байты 🙂 Радио занимало 19% времени, но генерировало лишь 0,3% байтов (аудио требует мало данных). Голосовая связь (телефон) — это всего 5% слов и ничтожная доля байтов, но это единственный полностью интерактивный канал до эпохи интернета. ТВ оставалось на 2008 год главным источником информации по времени (41% всех часов) и количеству слов (45%), однако по объему данных (байтам) телевидение занимало только второе место (35%), уступая компьютерным играм.

Вот с играми интересно. Главная находка отчета: Игры генерируют (или генерировали в 2008) 55% всех «байтов», потребляемых домохозяйствами. При этом они занимают лишь 8% времени пользователя. Это довольно спорная штука в их отчете.

Те 100500 слов — это оценка реальных слов, которые человек либо прочитал, либо услышал. Это не метафорический «эквивалент», а попытка подсчитать именно вербальную информацию. Они взяли время потребления каждого медиа и умножили на среднюю скорость поступления слов для этого канала. Чтение (книги, газеты, интернет-тексты): 240 слов в минуту. Электронная почта и веб-серфинг — 240 слов в минуту. Телевидение (диалоги в шоу/фильмах): 153 слова в минуту. Радио: 80 слов в минуту (меньше, так как много пауз и музыки). Музыка: 41 слово в минуту (тексты песен).

Ссылка в комментах

Ближе к каждому: персонализация видеорекламы на примере AI-тюторов | 2025-12-14T17:08:38

У меня долго крутилась реклама AI language tutor, на которую я не реагировал, и система не недельку про меня забыла и вернулась с тьютором заметно постарше.

Но вообще интересно, как скоро видеореклама для нас станет персонифицированной? Ну там в одной и той же рекламе ньюйоркцы будут видеть свой город, чернокожие — чернокожих, утром главная героиня будет пить кофе, а на фоне будет мелькать машина с логотипом родного университета?

Тоска по оригиналу: когда реставрации портят воспоминания | 2025-12-13T15:05:21

В советское время была хорошая школа анимации, на протяжении многих десятков лет она была ведущая в мире. Если сейчас набрать в youtube «Вовка в тридевятом царстве», то выдаются практически только реставрации 🤮 причем заодно выдаются такие же блевотные реставрации и кучи других мультиков, сделанные в том же духе (векторизацией, черные контуры). Если зайти на википедию, то там будет скриншот именно с реставрации, а не с оригинального мультика 1965 года. Оригинал можно найти например по запросу «вовка в тридевятом царстве мадина газиева», а вот по запросу «вовка в тридевятом царстве союзмультфильм 1965» не показывается он вообще.

Вообще поломали интернет.

P.S. Кстати, двое из ларца, исполняющие желания, и «так сойдет» очень резонирует с ChatGPT сегодняшнего дня 😉