Matlab: связывание идентификатора с набором данных (например, структурой)?

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

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

Итак, например, допустим, что это мой список этих конкретных узлов:

nodelist = [3 27 38] %(so these are my node ID's) 

Затем для каждого узла у меня есть следующий набор данных, связанный

a (scalar)
b (5x5 double matrix)
c (10x1 double vector)

(a total of 36 double values associated with each node ID)

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

Подход 1

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

size(2Dmat) = [length(nodelist), 36]

Скажем, я хотел получить доступ к b(3,3) для узла ID 27, я бы получил доступ к 2Dmat(2,14).

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

Подход 2

Другой подход заключается в использовании какой-то структуры для каждого узла в списке узлов:

a = 4.4;
b = rand(5,5);
c = rand(10,1);
s = struct('a',a,'b',b,'c',c)

И тогда я могу получить доступ к данным, например, через s.b(3,3) и т. д. Но я просто не знаю, как связать структуру с идентификатором узла?

Подход 3

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

Обратите внимание, что в конечном итоге я конвертирую код из Matlab в C/C++, поэтому я бы предпочел реализовать что-то, что не сильно зависит от некоторых специфических функций Matlab.

Итак, какие мысли о том, как реализовать эту функциональность чистым способом? Я надеюсь, что мой вопрос имеет смысл и спасибо заранее!


person Finnur Pind    schedule 14.04.2019    source источник
comment
Я бы по крайней мере рассмотрел возможность использования cells и cell arrays.   -  person High Performance Mark    schedule 14.04.2019
comment
Почему бы не добавить еще одно поле в структуру для идентификатора?   -  person beaker    schedule 14.04.2019
comment
Хотя теперь, когда я думаю об этом, возможно, карта с идентификатором в качестве ключа и индексом в массиве структур в качестве значения была бы лучше, поскольку это обеспечило бы более быстрый доступ.   -  person beaker    schedule 14.04.2019
comment
Можете ли вы использовать карту списков? Ключом является имя списка: mathworks.com/help/matlab/map-containers. .html   -  person duffymo    schedule 16.04.2019


Ответы (2)


Подход 2 является самым чистым и легко транслируется на C++. Для каждого узла у вас есть структура s, затем:

data(nodeID) = s;

это то, что называется структурным массивом. Вы индексируете как

data(id).b(3,3) = 0.0;

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


В C++ у вас будет вектор структур:

struct Bla{
   double a;
   double b[3][3];
   double c[10];
};

std::vector<Bla> data(N);

Or in C:

Bla* data = malloc(sizeof(Bla)*N);

(и не забудьте free(data), когда закончите).

Затем в C или C++ вы получаете доступ к элементу следующим образом:

data[id].b[2][2] = 0.0;

Перевод очевиден, за исключением того, что индексация начинается с 0 в C++ и с 1 в MATLAB.


Обратите внимание, что этот метод требует больших затрат памяти, чем подход 1 в MATLAB, но не в C или C++.

Подход 3 — плохая идея, он просто замедлит ваш код без каких-либо преимуществ.

person Cris Luengo    schedule 14.04.2019
comment
Я предполагал, что MATLAB выделит память для всех 38 элементов для идентификаторов [3 27 38], но, похоже, это не так. - person beaker; 15.04.2019
comment
@beaker: он выделяет для них элементы массива структур, но если вы ничего не присваиваете полю, это поле не занимает память. В любом случае, я предполагаю, что идентификаторы узлов следуют друг за другом в любом разумном приложении, в противном случае перенумеровать их тривиально. - person Cris Luengo; 15.04.2019
comment
Большое спасибо, Крис, это работает как шарм, именно то, что мне нужно. Могу я спросить вас, можно ли сделать что-то подобное в C, какой-то массив структур? До сих пор я работал с Matlab и C, так как я больше знаком с C, чем с C++. (Я всегда предполагал, что в какой-то момент мне придется переключиться с C на C++, но до сих пор мне удавалось этого избегать...) - person Finnur Pind; 15.04.2019
comment
@FinnurPind: я отредактировал ответ, указав C-эквивалент std::vector. Я перешел с C на C++ 4 года назад и никогда не хочу возвращаться. С++ намного проще и быстрее писать. Я не могу рекомендовать его достаточно. Но изучите C++11, забудьте обо всем, что было раньше. C++11 отличается от того, что было раньше. - person Cris Luengo; 15.04.2019
comment
Спасибо @CrisLuengo за редактирование и ваши идеи. Я явно должен совершить прыжок с C на C++! - person Finnur Pind; 15.04.2019

Я думаю, что самым чистым решением, учитывая несмежный набор идентификаторов узлов, будет подход 2 с использованием контейнер карты, где идентификатор вашего узла является ключом (то есть индексом) в карте. Это можно реализовать в MATLAB с помощью объекта containers.Map и в C++ с использованием контейнера std::map. Например, вот как вы можете создавать и добавлять значения к карте узлов в MATLAB:

>> nodeMap = containers.Map('KeyType', 'double', 'ValueType', 'any');
>> nodelist = [3 27 38];
>> nodeMap(nodelist(1)) = struct('a', 4.4, 'b', rand(5, 5), 'c', rand(10, 1));
>> nodeMap(3)

ans = 

  struct with fields:

    a: 4.400000000000000
    b: [5×5 double]
    c: [10×1 double]

>> nodeMap(3).b(3,3)

ans =

   0.646313010111265

В C++ вам нужно будет определить структуру или класс (например, Node) для типа данных, который будет храниться на карте. Вот пример (... обозначает аргументы, переданные конструктору Node):

#include <map>

class Node {...};   // Define Node class
typedef std::map<int, Node> NodeMap;  // Using int for key type

int main()
{
  NodeMap map1;

  map1[3] = Node(...);  // Initialize and assign Node object
  map1.emplace(27, std::forward_as_tuple<...>);  // Create Node object in-place
}
person gnovice    schedule 15.04.2019
comment
Хорошее решение, если идентификаторы должны быть произвольными числами (в этом случае даже не обязательно должны быть целыми числами). Но если можно определить идентификаторы так, чтобы они были последовательными или почти последовательными, то вы можете избежать накладных расходов (времени и пространства) карты. - person Cris Luengo; 15.04.2019