Re[27]: И еще рассуждения об ИИ
От: Sinclair Россия https://github.com/evilguest/
Дата: 12.02.26 06:34
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:


PD>Например, вот такое. Сразу скажу — я не автор и не проверял его работу. Просто взял вот тут в качестве примера

PD>Допустим, мне надо использовать этот код. Верен ли он алгоритмически (то есть делает ли то, что ему положено) — не обсуждаем. Меня интересуют лишь два вопроса

PD>Может ли произойти переполнение с плавающей точкой в этом коде ?

PD>Не возникнет ли тут деление на 0 ?
PD>Ну вот я Вам и не соврал. Все изложил, что знаю. Жду ответа

Да, оба риска в этом коде реальны, но зависят от входных данных (вашей матрицы). Разберём по пунктам.

1) Может ли быть переполнение double?

В вашем коде потенциально “опасные” места такие:

(а) Суммирование в Mul

y[i] = y[i] + (A[i][j] * x[j]);


Переполнение возможно, если либо A[i][j] очень большое, либо x[j] становится очень большим, либо сумма большого количества членов разгоняет y[i].

Но с вашей структурой матрицы (почти все элементы 0, небольшие числа около диагонали) само по себе Mul обычно не переполняет. Однако важно:

Метод Якоби может “разогнать” X, если матрица плохо обусловлена, не диагонально преобладающая, или есть маленькие диагональные элементы.

Тогда x[j] может стать огромным, и уже A[i][j] * x[j] / суммы могут уйти в inf.

(б) Внутри Якоби
TempX[i] = F[i] - Σ_{g≠i} A[i][g]*X[g];
TempX[i] /= A[i][i];


Если A[i][i] маленькое по модулю, то деление сделает TempX[i] очень большим, и дальше это может быстро привести к inf.

А при Леннард–Джонсе?

Если вы уже записываете в матрицу готовые “небольшие” числа и для дальних пар ставите 0 — тогда переполнение именно из-за R^-12 в этом коде не случится, потому что здесь R не вычисляется.

Но если в другом месте вы считаете

U = E0*[(R0/R)12-(R0/R)6]

то переполнение возможно при очень маленьком R (почти совпадающие координаты, ошибка данных): (R0/R)12 может стать гигантским. Это уже не про Якоби, а про подготовку данных.

Итог по переполнению:
В этом коде переполнение double возможно если:

— Якоби расходится (что бывает часто, если нет диагонального преобладания/хороших свойств матрицы),
— или на диагонали очень маленькие числа → X улетает,
— или в матрице/векторе всё-таки встречаются большие значения.

2) Будет ли деление на 0?

Да, может. Единственное деление тут:
TempX[i] /= A[i][i];


Если хоть один диагональный элемент A[i][i] == 0, будет деление на ноль → получите inf или nan (в зависимости от платформы/режима FPU), а итерации дальше почти гарантированно “сломаются”.

Ещё важнее: даже если A[i][i] не ровно 0, но очень мал (например, 1e-300), то это практически то же самое по эффекту: TempX[i] станет огромным, и дальше возможны inf/NaN.

Что стоит сделать минимально (практично)

Перед итерациями (или внутри) проверять диагональ:

if (A[i][i] == 0.0) { /* ошибка */ }



лучше так:

if (fabs(A[i][i]) < 1e-15) { /* слишком близко к нулю */ }


Проверять появление nan/inf:

if (!isfinite(TempX[i])) { /* аварийно остановиться */ }


(Не по вашему вопросу, но критично) освобождение памяти у вас неправильное:

delete x; delete y;


для массивов нужно:

delete[] x;
delete[] y;


И для матрицы тоже нужно освобождение по строкам + массив указателей, иначе утечка.


PD>А пока что могу сказать, что было у меня. Просто убрал это стандартное корректирующее действие и стал ждать, что из этого выйдет. Примеры запускал (считайте, что тесты) Когда переполнение наконец произошло, нашел его место и поставил проверку. Вот и все. Место такое нашлось одно за все время, что я с программой работал. Нашлось бы еще одно — поставил бы проверку и там. И это при (будем так условно считать) полном непонимании этого алгоритма — я и впрямь в деталях той реализации разбираться не стал.

Как видно, запускать и ждать теперь необязательно

PD>Нет, не придется мне коммитить код в Ваш проект. Да и в любой другой тоже. Моя программистская карьера завершена. Но в ней я такое (да и не такое!) не раз коммитил, и как-то все потом благополучно работало и никто не жаловался.

Так же говорят тетеньки, которые пирожками на вокзале торгуют


PD>В теории Вы правы. Если мой код действительно в библиотеке, да еще рассчитанной на то, что ее будет использовать потом широкий круг пользователей, то такое недопустимо.

PD>Но где я сказал, что этот код для библиотеки ? Я такого нигде не говорил.
PD>Где я сказал, что этот код может быть, когда-то будет использоваться кем-то для иной цели ? Я такого тоже не говорил.
Это зависит не только от вас.

PD>Или в моем примере с Якоби — меня абсолютно не интересует, что будет, если какой-то другой пользователь начнет использовать этот код для каких-то матриц, в которых иные данные (ну скажем, много больших элементов далеко от главной диагонали). Это его проблемы. В той задаче, что я сейчас решаю, данные именно такие и другими быть не могут — объяснение выше.


Полностью с вами согласен. То, что вы описываете, называется "инженерная культура". Точнее — её отсутствие. Типа, давайте не будем клеить обои за шкафом. Если кто-то когда-то решит передвинуть шкаф — это будут его проблемы.

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

Да, совершенно верно. Именно так пишут школьники: главное, чтобы программа как можно скорее заработала. Скомпилировалась — полдела сделано. Запустилась и не вылетела — всё, несём сдавать.
Если преподаватель не проверил, что она ещё и делает то, что нужно — это его проблема. После получения зачёта эта программа всё равно пойдёт в корзину.

Но поскольку мир разработки не исчерпывается одноразовыми студенческими программами, мы своих студентов стараемся учить, как делать правильно. Наш лозунг: "Делайте всё как следует. Стрёмно получится само".

PD>Не слишком ли мы заботится о том, что код, вовсе не предназначенный для массового использования , не будет совершенно корректно работать для случаев, которые мне при его написании для данной задачи совершенно неинтересны ?

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

PD>Я Вам дал на естественном языке информацию об ожидаемом диапазоне данных во входной матрице. Другой информации у меня нет.

Для традиционных формальных средств этого недостаточно. А для ИИ — вполне.

PD>Те, кто отводил под это 2 цифры в 1960 или 1970 году — вполне были правы. Не дожили их машины и и их программы до 2000 года. И не могли дожить.

Прекрасно дожили. То, что программы, разработанные в СССР, выбрасывали вместе с машинами — это не стандартная ситуация, а глобальная катастрофа.
В развитом мире код, написанный для IBM360, прекрасно работал в 2010 (и наверняка сейчас всё ещё работает).

PD>А если какой-то их код и дожил года до так 1980, то его потом все равно переносили, там и должны были поправить. Там скорее всего многое пришлось бы править — ну хотя бы из-за разных размеров данных на разных машинах.

Про вашу точку зрения ещё в 1981 году сняли анимационный художественный фильм.
https://www.kinopoisk.ru/film/257212/
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.