Проверка правильности аргументов с плавающей запятой

Я хочу написать класс, представляющий цепь Маркова (назовем его MC). У него есть конструктор, который принимает матрицу перехода состояний (то есть vector<vector<double>>. Я полагаю, неплохо проверить, действительно ли это матрица (имеет одинаковое количество строк и столбцов) и действительно является матрицей перехода: все числа в нем являются вероятностями, то есть не меньше 0.0 и не больше 1.0, и для каждой строки сумма ее элементов равна 1.0.Однако есть проблема, возникающая из-за ограничений с плавающей запятой: например, сумма 0.3 + 0.3 + 0.3 + 0.1 не будет равно 1.0, так что проверить будет не так просто, поэтому я вижу два возможных решения этой проблемы:

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

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


person karlicoss    schedule 13.04.2013    source источник
comment
1-й лучше. 2-й — это реальный мир, и под этим я подразумеваю, что у нас много плохого кода по всему миру. Кодируйте в обороне, если вам не все равно. Не надо, иначе.   -  person Alexey Frunze    schedule 13.04.2013
comment
Очевидно, что первый вариант лучше, однако я бы предложил добавить тонкость, если небольшие отклонения от критериев могут стать проблемой: у конструктора может быть опция, которая позволяет ему хранить скорректированную матрицу перехода, которая имеет один или несколько из элементы в каждой строке изменены так, чтобы сумма элементов была как можно ближе к 1.0. В популярных библиотеках есть прецеденты, например. функции, которые принимают массив вероятностей a и нормализуют каждый элемент массива, чтобы он был равен a[i]/sum(a), чтобы иметь дело со случаем, когда элементы a не добавляются к 1.0.   -  person Simon    schedule 13.04.2013
comment
@simon ясно? Выглядит ясно в другую сторону для моих глаз.   -  person David Heffernan    schedule 13.04.2013
comment
@DavidHeffernan: я обнаружил, что если ошибочный ввод в функцию приводит к тому, что функция немедленно выдает исключение или аварийно завершает работу, это значительно упрощает поиск ошибок, вызвавших ошибочный ввод. Если первые признаки проблемы не обнаруживаются долгое время спустя, может потребоваться много времени для отладки, чтобы найти основную причину. Там, где проверка непротиворечивости вызывает слишком много накладных расходов для производственного кода, я отключу проверку для рабочей среды, но, если это не слишком замедляет работу, я предпочитаю оставить проверку и в рабочем коде.   -  person Simon    schedule 13.04.2013
comment
@Simon: Что значит неисправный? В какой момент вы объявляете что-то неисправным?   -  person tmyklebu    schedule 13.04.2013
comment
@tmyklebu: Учитывая функцию (в данном случае конструктор), которая требует, чтобы ее входные данные соответствовали указанным критериям, любой ввод, который не соответствует этим критериям, является ошибочным. Если функция может определить, что входные данные не соответствуют ее требованиям, и может немедленно выдать исключение, мне кажется, что это более быстрый способ выяснить, почему входные данные не соответствуют требованиям функции, чем ждать, пока функция создаст некоторое неопределенное поведение.   -  person Simon    schedule 13.04.2013
comment
@simon в этом сценарии кажется невозможным прийти к соглашению об определении ошибочного, и когда это так, мой опыт подсказывает мне, что попытка проверки приведет к ложным срабатываниям и неуравновешенным пользователям.   -  person David Heffernan    schedule 13.04.2013
comment
@simon Если функция может определить, что ввод не соответствует ее требованиям. Тогда вперед. Напишите код, который отделяет хороший ввод от плохого.   -  person David Heffernan    schedule 13.04.2013
comment
@tmyklebu: я должен сказать, что подход, который вы использовали в своем ответе, дает много преимуществ. В частности, если классу недостаточно известно о том, как он будет использоваться для определения критериев, которым должны соответствовать его входные данные, он, безусловно, должен оставить это решение пользователю класса. Однако мне показалось, что в этом случае были некоторые критерии, которым класс мог обоснованно ожидать, что входные данные удовлетворят.   -  person Simon    schedule 13.04.2013
comment
@DavidHeffernan: я недостаточно знаю о точных требованиях, которым должны удовлетворять входные данные для класса OP. Как я заметил в другом комментарии, я полностью согласен с подходом, описанным в ответе tmyklebu, когда у класса нет достаточной информации, чтобы отделить хороший ввод от плохого. Тем не менее, я провел достаточно времени, отслеживая ошибки с момента их появления до точки их возникновения в удаленных фрагментах кода, чтобы по возможности свести к минимуму этот опыт в будущем.   -  person Simon    schedule 13.04.2013
comment
@simon вот в чем суть. Вот только непонятно, что здесь хорошо, а что плохо.   -  person David Heffernan    schedule 13.04.2013
comment
@DavidHeffernan: если кажется невозможным прийти к соглашению об определении ошибки, я соглашусь с вами в том, что попытка проверки приведет к ложным срабатываниям и неуравновешенным пользователям.   -  person Simon    schedule 13.04.2013
comment
@Simon: Любое понятие неисправности здесь будет иметь следующую проблему: я могу взять что-то, что не является неисправным, сделать с этим что-то вполне разумное и получить что-то неисправное. Вот почему вы избегаете объявлять что-то неисправным. Дело не в том, что мы не уверены, где должна быть граница; дело в том, что если мы поместим где-нибудь такую ​​границу, мы сделаем код более бесполезным.   -  person tmyklebu    schedule 13.04.2013


Ответы (1)


Делай второй.

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

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

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

Теперь подумайте об операции «имея цепь Маркова и целое число k, вернуть цепь Маркова, образованную путем повторения входной цепи k раз». Вы можете видеть, что это будет накапливать округление и страдать от тех же проблем, что и «распределение вероятности попадания с цепью Маркова».

Разве не было бы отстойно, если бы у вас был выбор только между вещами, которые ломаются после 12 часов использования, и вещами, которые излишне неточны?

(Проверка прямоугольности и матричности аргумента квадратной матрицы, конечно, вполне разумна.)

person tmyklebu    schedule 13.04.2013
comment
+1 пользователь вашего кода ничего не сможет получить, если отправит фиктивные входные данные, поэтому позвольте им позаботиться о том, чтобы все было правильно - person David Heffernan; 13.04.2013
comment
Может иметь вспомогательную функцию в классе, называемую check(), или что-то, что дает результат оценки (или true/false) в зависимости от того, является ли это хорошей матрицей или нет. - person Mats Petersson; 13.04.2013