Первобытное мышление или история магической единицы

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

В своей книге "Первобытное мышление" Люсьен Леви-Брюль рассказывает, как люди из первобытных обществ, даже прожившие долгое время в цивилизованном обществе, продолжают верить в колдунов и магию. Его теория состояла в том, что первобытные народы склонны к так называемому дологическому мышлению, тогда как представители современной цивилизации опираются в своих суждениях на логику. Леви-Брюль, однако, ошибался. Ниже я покажу, как люди из цивилизованного общества, получившие образование в ведущих ВУЗах страны, начинают верить в магию даже в той области, в которой они являются профессионалами. Имена, названия и высказывания изменены так, чтобы никто не нашёл себя и не обиделся.

Работал я тогда в компании среднего размера. Несмотря на средний размер, это была компания, которая продавала свой продукт в передовые страны запада, а это всё-таки показатель высокого качества. И работали там не простые программисты, а выпускники мехмата МГУ, МФТИ и прочих уважаемых ВУЗов. Но был в этой компании момент, который меня конкретно выбешивал: мне постоянно повторяли, что без особой необходимости не надо ничего менять. Как-то, помнится, нашёл я в коде строку

x = (a + b * c + d) * (g - sin(alpha) * cos(beta) ) * (cos(d5) + d8 + d11) / (k - t * p) / (d7 + d9 + d15);
y = (a + b * c + d) * (g - sin(alpha) * cos(beta) ) * (cos(d5) + d8 + d11) / (k - t * p) / (d7 + d9 + d15);
z = (a + b * c + d) * (g - sin(alpha) * cos(beta) ) * (cos(d5) + d8 + d11) / (k - t * p) / (d7 + d9 + d15);

Ну я взял и поменял на

x = y = z = (a + b * c + d) * (g - sin(alpha) * cos(beta) ) * (cos(d5) + d8 + d11) / (k - t * p) / (d7 + d9 + d15);

Не ускорения ради, а просто чтобы не мозолило глаз. Как на меня тогда напал наш группенфюрер...

Сейчас все модели сломаются! Потом скажут, что мы всё сломали! Я такие изменения не пропущу!

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

if (fabs (mult - 1.) > std::numeric_limits::epsilon ())  // this is needed because x * 1. != x if x << 1
{
    factor *= mult;
}

Как мы видим, стоит проверка, что если отличие множителя от единицы меньше численной точности представления (примерно 16 знаков для double), то умножение не производится. С точки зрения любого разумного человека эта проверка является совершенно бессмысленной, хоть и довольно безвредной (если конечно не считать вредом то, что людей заставляют верить в магию умножения на единицу). Однако автор кода оставил для нас комментарий, почему эта проверка важна: мол если множитель равен 1, то лучше не умножать, потому что это может изменить умножаемое число, если это число мало. Писал, очевидно, человек, который не знает, что в представлении с плавающей запятой числа хранятся так, что их относительная точность не зависит от того, равно число 1.e+100 или 1.e-100.

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

Множитель mult в вышеозначенном коде ввёл некто, назовём его Вова, в процессе реализации нового функционала. В старых моделях этот множитель всегда равнялся единице и никак не должен был повлиять на их работу. Однако через какое-то время было обнаружено, что падает одна из моделей. Стали выяснять почему и оказалось, что движок требовал памяти больше, чем имелось на машине, в результате чего программа падала. Как часто бывает в таких ситуациях, когда умные люди, не имея дельных предложений, молчат, находится ограниченный, который предлагает какую-то херню. И такой человек немедленно нашёлся. Назовём его Коля. Коля "исправил" проблему: при превышении размера имеющейся памяти программа стала корректно завершаться, а не просто падать. Кроме этого весомого вклада в решение Коля обратил внимание, что падающая модель невероятно сложна: содержит аж на три элемента больше, чем другие модели, солвер не справляется и давайте использовать магическое слово "СЛОЖНАЯЗАДАЧА" для решения таких моделей. Как также обычно бывает, вместо того, чтобы сказать ограниченному, что он ограниченный, большинство соглашается с его предложениями, а умные люди продолжают молчать. Но оказалось, что модель всё равно продолжала падать, правда теперь уже на кластере.

Тут, как в книге Леви-Брюля, впору бы уже признать, что виноват колдун, который наслал порчу на кластер, но главный эксперт по кластерам Алик понял, что причина падения в том, что кластер пытался выделить слишком много памяти на NUMA-узле (хм... мне кажется, мы уже где-то это видели), и пообещал придумать, как защитить кластер от этого хитрого колдунства.

В этот момент вмешался технический директор и начал спрашивать, что мол никого не удивляет, с какого перепуга узлу потребовалось 70 гигов памяти, но ему быстро объяснили, что причина в страшной модели (в сочетании со страшным колдунством конечно же), и он перестал мешать людям бороться с колдунством: в модели что-то подкручивали, что-то меняли, но колдунское проклятие было слишком сильно и ничего не помогало. К концу года проблему так и не победили и просто перестали обращать на неё внимание. Однако через несколько месяцев (!) некто, назовём его Муся, сделал наброс на вентилятор, написав, что мол Вова своим множителем сломал модель. Это ключевой момент в истории, поэтому обратим внимание терпеливого читателя на то, что модель и без изменений Вовы не работала уже больше года. Просто после изменений Вовы она стала выплёвывать другие ошибки.

По-хорошему Вова мог бы призвать участников дискуссии к разуму и объяснить, что его изменения вообще не затрагивали тот кусок кода, где происходило падение, и что оно происходило и до его изменений. Но какой разум с этими людьми? И Вова нашёл наилучшее решение проблемы: поставил условие, что если множитель равен единице, то не надо домножать на этот множитель. И заодно вкатил туда псевдонаучное объяснение, что мол x * 1. != x. Однако это бессмысленное изменение сделало главное: проблемная модель снова стала глючить точь-в-точь, как глючила до этого.

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

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

P.S. Вот из-за таких случаев я всё больше утверждаюсь во мнении, что образование в ведущем ВУЗе вообще не гарантирует, что человек умный. Иногда я вижу перед собой обезьян, которые умеют решать дифуры.

Источник: https://habr.com/ru/articles/784112/


Интересные статьи

Интересные статьи

В марте 1878 года Дервиш-паша, командующий турецкими войсками, вручил князю Святополк-Мирскому ключи от города Батуми, а уже в июне того же года Берлинский конгресс утвердил статус по...
С самого начало истории человечества  люди хотели жить в комфортных условиях: спать по ночам и не просыпаться от невыносимой жары или не стучать зубами, закутавшись ...
В Unix-подобных операционных системах общение программы с внешним миром и операционной системой происходит через небольшой набор функций — системных вызовов. А значит, в отладочных целях полезн...
Возможно вы уже читали мои статьи: Научные мультфильмы, что показывать детям без вреда для них? Простые опыты с ребенком дома Как мы для музея науки сделали самую большую катушку Тесла в У...
Однажды фронтендеру позвонил дизайнер и попросил сделать «паутинку» за запотевшим стеклом. Но потом оказалось, что это не «паутинка», а гексагональная сетка, и не за стеклом, а она уходит вдаль, ...