Реализация матрицы, которая более эффективна - с использованием массива массивов (2D) или массива 1D?

При реализации матрицы Matrix с использованием массивов, которая была бы более эффективной? Использование массива 1D или массив массивов (2D)?

Я бы подумал, что 2D более эффективен, так как у вас уже есть координаты X и Y элемента, где в 1-й реализации вам нужно вычислить индекс.

Изменить: он реализуется с использованием Java

Ответ 1

"Эффективный" не является обязательным для всех.

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

int *x[9];

где каждый "int *" будет выделяться отдельно.

2D-массив (который не обязательно является массивом массивов) будет, как правило, более быстрым (эффективным с точки зрения скорости), поскольку он выстраивает ячейки памяти с помощью математики без необходимости удаления ссылок на ячейки памяти. Я говорю о конструкции:

int x[9][9];

1D массив формы:

int x[81];

вряд ли будет быстрее, чем эквивалентная 2D-версия, так как вам все равно придется делать вычисления в какой-то момент, чтобы найти правильную ячейку (вручную в коде, а не позволить компилятору сделать это).

После редактирования, где Java добавлено как требование:

Я считаю, что 2D-массивы Java относятся к массиву многоадресных массивов (для чего потребуется два обращения к памяти, а не один, необходимый для 1D-массива), поэтому 1D-массив с ручным индексированием может быть быстрее. Итак, вместо объявления и использования:

int x[width][height];
x[a][b] = 2;

вы можете получить больше скорости с помощью:

int x[width*height];
x[a*height+b] = 2;

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

Эта разница в скорости основана на том, как я думаю, что Java закодирована под обложками, поэтому я могу ошибаться (но я сомневаюсь в этом:-). Мой совет, как всегда, для вопросов оптимизации, измерьте, не угадайте!

Ответ 2

Я собираюсь сломать ряды с ответами на сегодняшний день и предложить следующую причину: 1D-массив, скорее всего, быстрее.

2D-массив включает в себя 2 обращения к памяти. A [x] [y], например, сначала должен искать A [x], а затем выполнить другой поиск этого массива [y].

Традиционно реализация 1D будет A [x + (ширина * y)]. Когда ширина указана в регистре (или в буквальном значении), это означает, что 2 математики и 1 поиск вместо двух поисковых запросов. Поисковые запросы на порядок медленнее, чем математические операционные системы, поэтому, если ширина в регистре составляет даже небольшой процент времени или является литералом, это будет быстрее.

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

Ответ 3

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

sum(double[], int): 2738 ms (100%)
sum(double[,]):     5019 ms (183%)
sum(double[][]):    2540 ms ( 93%)

Ястребиные массивы являются самыми быстрыми, за ними следуют 1-мерные массивы, за которыми следуют многомерные массивы. Ястребиные массивы, являющиеся самыми быстрыми, вероятно, не то, что люди предсказывали бы. Эти результаты, вероятно, бесполезны для Java, поскольку Java имеет разные оптимизации (и не многомерные массивы в Java).

Я бы очень осторожно делал предположения. Например, если вы зацикливаете на строку 2D-массива, Java может оптимизировать поиск индекса или выйти за пределы проверки, что это может быть не так возможно, если вы используете 1D-массив с вычислениями inline-индекса.

Я предлагаю написать простую программу для проверки скорости на желаемой платформе.

Ответ 4

В зависимости от языка разница не будет. Реальный вопрос заключается в том, как распределяется 2D-матрица. Является ли это единым непрерывным пространством X * Y байтов или он выделяется как Y независимых массивов размера X. Последнее обычно делается при создании разреженных матриц.

Ответ 5

Коммерческий пакет конечных элементов, который я использовал во время моей карьеры инженера-механика с использованием 1D-массива в качестве основы для его вычислений линейной алгебры. Методы конечных элементов приводят к крупным, разреженным и полосчатым матрицам. Хранение всех этих нулевых элементов за пределами группы не имело смысла.

Единственный раз, когда вы будете использовать 2D-массивы, - это небольшие, академические проблемы или проблемы, которые не разрешены (например, методы граничных элементов).

Ответ 6

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

  • Меньше кода → меньше времени для его написания
  • Меньше строк кода означает меньше ошибок (поскольку ошибки для KLOC довольно постоянны для заданного программиста), которые легче найти (поскольку они не могут скрыть хорошо в нескольких строках кода)
  • Если ваш алгоритм не подходит для задачи, его проще заменить, если вместо него 10 строк вместо 100
  • Компактная реализация обычно приводит к чистому интерфейсу, который делает код, который использует библиотеку чистым (так что эффект умножается). Это также может привести к более сложной реализации библиотеки, если это сделает код клиента более эффективным (т.е. Вы сосредоточите усилия в одном месте, чтобы сделать все остальное более простым).

Это также сильно зависит от шаблонов доступа. Вы всегда идете по всей матрице? Разве это редко? Вы предпочитаете обрабатывать строки или столбцы?

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

Если ваши проблемы требуют найти строку/столбец, а затем обработать эти значения, было бы более эффективно использовать 2D-подход, поэтому первый доступ возвращает массив, который вы затем можете обрабатывать, не беспокоясь о границах, одноразовый -errors и т.д.