Устранение ошибок округления из матрицы

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

Затем я умножаю всю матрицу на число от 0 до 1 (например, 1/6) и округляю все числа до x десятичных знаков. Теперь я не могу быть уверен, что сумма строк и столбцов будет равна нулю. Я хочу, чтобы суммы снова были равны нулю с наименьшей возможной корректировкой (или, по крайней мере, очень маленькой корректировкой)

Есть ли алгоритм, который может решить такую ​​проблему?

Пример (очень простой): матрица:

    200  -200  0

    400  400  -800

   -600 -200  800

round2 ((1/6) * матрица)

33.33  -33.33  0   

66.67  66.67   -133.33

-100   -33.33  133.33

person user1879280    schedule 05.12.2012    source источник
comment
Я бы просто сложил строки и столбцы и, вместо того, чтобы проверять, равны ли они нулю, проверить, меньше ли абсолютное значение суммы определенного допуска - в этом случае, возможно, abs(sum) <= 0.01   -  person Blazemonger    schedule 05.12.2012
comment
Это НЕ вопрос алгоритма. Вы вводите проблему путем округления, и независимо от того, как вы ее исправляете, вы будете вводить другие проблемы, например, нарушение симметрии между определенными элементами в матрице. Разве нельзя ограничить округление только отображаемыми значениями, сохранив при этом полное значение для математической обработки? У вас все равно будет некоторый «шум», делающий суммы потенциально ненулевыми, но с этой проблемой вы должны справиться, определяя ноль как меньший, чем некоторый допуск.   -  person Bert te Velde    schedule 05.12.2012


Ответы (4)


Это не решение; просто более математическое описание того, чего вы пытаетесь достичь (без оценки того, правильно ли это делать):

Поскольку вы округляете все числа до десятичных знаков x, мы можем рассматривать эти числа как целые (просто умножьте их на 10 ^ x).

Теперь вы пытаетесь решить следующую проблему:

Учитывая матрицу

A11+Adj11   A12+Adj12   ...   A1n+Adj1n
A21+Adj21   A22+Adj22   ...   A2n+Adj2n
A31+Adj31   A32+Adj32   ...   A3n+Adj3n
...         ...         ...   ...
Am1+Adjm1   Am2+Adjm2   ...   Amn+Adjmn

Где A11..Amn - постоянные целые числа,

Найти целые числа Adj11 ... Adjmn

Минимизирующая сумма (абс (Adjxy))

(или, может быть, вы предпочитаете: Минимизирующая сумма ((Adjxy) ^ 2)

При условии:

- for each row m: Adjm1+Adjm2+...+Adjmn = - (Am1+Am2+...+Amn)
- for each col n: Adj1n+Adj2n+...+Adjmn = - (A1n+A2n+...+Amn)

Это проблема целочисленного программирования с m * n переменными и m + n ограничениями. Функция, которую вы пытаетесь минимизировать, не является линейной.

Боюсь, что эта проблема далеко не тривиальная. Я считаю, что вам лучше разместить его на https://math.stackexchange.com/

person Lior Kogan    schedule 05.12.2012

То, что вы здесь испытываете, по сути, является ошибкой точности. Вы ничего не можете сделать, если совсем не округлите. Это похоже на сохранение фотографии как 256-цветного изображения. Вы теряете информацию (по сути, точность; из-за дискретности) и ничего не можете сделать. Для изображений существуют алгоритмы, позволяющие сделать изображения более гладкими / близкими к оригиналу (например, дизеринг), но у вас нет таких вещей для однозначных чисел.

Возможные решения (на самом деле только одно с двумя разными способами визуализации):

  • Только круглый для показа. Пользователь должен иметь возможность интерпретировать, что числа усечены / округлены. В вашем примере должно быть очевидно, что 6.67 на самом деле будет 6.66666....

  • Не округляйте вообще, а просто обрежьте числа после фиксированного числа десятичных знаков (при необходимости добавьте ...; это фактически похоже на другое решение).

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

person Mario    schedule 05.12.2012
comment
В целом хороший совет, но тут ничего не поделаешь, это преувеличение. Вы могли бы, например, ищите решение методом наименьших квадратов, то есть комбинацию корректировок, которая приводит к суммированию строк и столбцов до 0 и минимизирует сумму квадратов разностей. - person j_random_hacker; 05.12.2012
comment
Возможно, я бы сказал, что вы все равно просто переместите задачу, и, если вам не повезет, ваш расчет станет намного более сложным и зависящим от случая. - person Mario; 06.12.2012

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

person Mark Ransom    schedule 05.12.2012

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

С помощью одномерного вектора [a[1], a[2], ..., a[n]] вы можете вычислить частичные суммы [a[1], a[1]+a[2], ..., a[1]+a[2]+...+a[n]], умножить его, а затем восстановить хороший вектор, вычтя предыдущую ячейку из текущей: [a[1]*b, (a[1]+a[2])*b-a[1]*b, ..., (a[1]+a[2]+...+a[n])*b-(a[1]+a[2]+...+a[n-1])*b]. При использовании этого трюка ошибка любой частичной суммы не превышает 10 ^ (- x).

Вы можете адаптировать этот метод для двухмерной матрицы с помощью трех следующих процедур:

partial_sum(M) =
  for i = 0 to n-1 do
    for j = 1 to m-1 do
      M[i][j] += M[i][j-1]
    done
  done
  for i = 0 to n-1 do
    for j = 1 to m-1 do
      M[j][i] += M[j-1][i]
    done
  done

multiply(M, a) =
  for i = 0 to n-1 do
    for j = 0 to m-1 do
      M[i][j] *= a
    done
  done

restore(M) =
  for i = 0 to n-1 do
    for j = 1 to m-1 do
      M[i][j] -= M[i][j-1]
    done
  done
  for i = 0 to n-1 do
    for j = 1 to m-1 do
      M[j][i] -= M[j-1][i]
    done
  done
person Thomash    schedule 05.12.2012