AnyLogic
Развернуть
Размер шрифта

Массивы

Решение некоторых проблем требует задания многоразмерных данных. Как и большинство других инструментов системной динамики, AnyLogic поддерживает переменные-массивы.

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

Массивы используются, когда:

  • Нужно хранить большое количество коэффициентов.
  • Нужно создать многоуровневую модель.

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

С помощью же массивов вы можете создать такую многоуровневую модель на одной диаграмме. Рассмотренные характеристики удобно описываются с помощью перечислений: вместо того, чтобы создавать несколько различных моделей (по модели на каждую определенную группу населения), вы можете просто создать массив со следующими размерностями: Gender(male, female), Age(child, teenager, adult, aged) и SocialGroup(wealthy, middleclass, deprived). Такая модель будет более компактной, и производимые вами изменения будут касаться не одного уровня, а всей многоуровневой модели.

Массивами в AnyLogic могут быть объявлены накопители, потоки, динамические переменные, а также параметры.

Демо-модель: Bass Diffusion Arrays Открыть страницу модели в AnyLogic Cloud. Там можно запустить модель или скачать ее по ссылке Исходные файлы модели.

Задание переменной-массива

Чтобы объявить переменную массивом

  1. Выберите переменную в графическом редакторе или в панели Проекты.
  2. Чтобы сделать эту переменную массивом, установите флажок Массив в панели Свойства.
  3. Вы можете увидеть, что за именем переменной в графическом редакторе теперь будут следовать квадратные скобки, обозначающие то, что данная переменная является массивом.

    Переменные типа массив в графическом редакторе

  4. Щелкните по ссылке {...} справа от флажка. При этом откроется секция свойств Размерности массива.
  5. Задайте размерности этой переменной-массива. Добавьте нужные вам размерности из списка Доступные размерности в список Выбранные размерности. Чтобы добавить размерность, выберите ее в списке Доступные размерности и щелкните по кнопке . Чтобы добавить все размерности, щелкните по кнопке .
  6. При необходимости вы можете удалить ранее добавленную в массив размерность. Чтобы удалить размерность, выберите ее в списке Выбранные размерности и щелкните по кнопке . Чтобы удалить все размерности, щелкните по кнопке .
  7. Порядок задания размерностей играет большую роль, поскольку именно в таком порядке вы будете указывать значения элементов при инициализации массива и т.д. Вы можете изменить порядок следования размерностей с помощью кнопок и .

Инициализация массива

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

AnyLogic предоставляет пользователям простой и удобный редактор начальных значений массива. Это сделано потому, что процесс инициализации многомерных массивов является нетривиальной задачей, прежде всего потому, что не существует простого и наглядного способа представления на плоскости многомерных данных.

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

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

Чтобы открыть редактор начальных значений массива

  1. Выберите переменную или параметр типа массив в графическом редакторе или в панели Проекты.
  2. Перейдите в панель Свойства.
  3. Щелкните мышью по кнопке Редактировать....
  4. Откроется окно редактора начальных значений массива. Редактор состоит из двух панелей. В левой панели редактора задается срез массива, элементы которого вы хотите проинициализировать. В правой панели отображается таблица, в ячейках которой вы собственно и задаете значения элементов выбранного среза массива.

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

Чтобы проинициализировать все элементы массива одним значением

  1. Откройте редактор начальных значений этого массива.
  2. Убедитесь в том, что в столбце Элементы расположенной слева таблицы во всех ячейках выбрано значение [ВСЕ].
  3. Уберите галочки из обоих столбцов таблицы — как , так и .
  4. Введите значение, которым вы хотите проинициализировать все элементы этого массива, в расположенной справа таблице.

  5. Когда вы закончите, щелкните мышью по кнопке OK.

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

Предположим, вы создаете модель, изучающую динамику изменения численности населения в Москве и Санкт-Петербурге. Пусть люди в нашей модели условно разделяются по трем характеристикам: полу, возрасту и месту проживания. Эти характеристики удобно описываются с помощью перечислений: Пол(мужчина, женщина), Возраст(ребенок, подросток, взрослый, пожилой) и Регион(СПб, Москва).

Давайте рассмотрим, как можно проинициализировать массив с тремя размерностями Население[Регион, Возраст, Пол].

Чтобы проинициализировать элементы массива различными значениями

  1. Откройте редактор начальных значений этого массива.
  2. Вы увидите, что две размерности в расположенной слева таблице будут выделены галочками — одна в столбце , а другая — в столбце . Так вы можете выбрать, элементы каких размерностей вы будете задавать в таблице справа. Элементы размерности, выбранной в столбце , будут отображаться в ней в качестве строк, а размерности выбранной в столбце - в виде столбцов:

  3. Если вы сейчас будете вводить значения в расположенной справа таблице, то они будут присваиваться всем соответствующим элементам остальных размерностей массива. То есть, если вы введете, например, 1000000 в ячейке взрослый - СПб, то тем самым вы проинициализируете этим числом и количество взрослых мужчин и количество взрослых женщин, проживающих в Санкт-Петербурге.
  4. Если же вы хотите задавать различные значения для элементов размерности Пол, то нужно будет ее зафиксировать, как мы говорили ранее.
  5. Давайте для начала проинициализируем количество мужчин. Для этого выберите в левой таблице мужчина в ячейке Пол - Элементы. Теперь в расположенной справа таблице вы можете задавать количество мужчин в вашей модели — в каждой ячейке будет задаваться значение соответствующего элемента массива, например, в ячейке пожилой - Москва будет задаваться количество мужчин преклонного возраста, проживающих в Москве. Задайте все значения согласно имеющимся у вас данным:

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

  7. Когда вы закончите, щелкните мышью по кнопке OK.

Вы можете проверить, правильно ли вы задали начальные значения с помощью инспектирования значения переменной во время выполнения модели:

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

Задание уравнений для массивов

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

Задание одного и того же уравнения для всех элементов массива

В простейшем случае для всех элементов массива задается одно и то же уравнение, возможно, ссылающееся на размерности массива. Например, если накопитель Population является массивом с размерностями:

Region = {NORTH, SOUTH} и Gender = { MALE, FEMALE } то уравнение может выглядеть так:

d(Population[Gender, Region])/dt = Births[Gender, Region]-Deaths[Gender, Region]

Чтобы задать одно и то же уравнение для всех элементов массива

  1. Выделите переменную-массив в графическом редакторе и перейдите в панель Свойства.
  2. Как и в случае системно динамической модели со скалярными переменными, так и в случае модели с массивами, формула, определяющая, как значения элементов накопителя меняются с течением времени, по умолчанию не редактируется — она создается автоматически, в соответствии с потоками, втекающими в накопитель и/или вытекающими из него. Это так называемый классический режим задания уравнения, подробнее можно прочесть здесь. На рисунке ниже вы можете увидеть формулу для накопителя Population из рассматриваемого нами примера.

  3. Но если вам нужно изменить формулу накопителя, то вы можете выбрать в группе кнопок Режим задания уравнения опцию Произвольный - тогда вы сможете редактировать формулу, задающую, как меняются значения элементов накопителя.

  4. Введите формулу, вычисляющую значение переменной, в поле d(<имя массива>)/dt = . Если, например, значение переменной Births одинаково и для MALE, и для FEMALE, и различается только по регионам (так, что переменная Births представляет собой массив с одной размерностью Region), то вы можете написать:

    Это уравнение внутри AnyLogic будет приведено к следующему простейшему циклу:
    for( r : Region )
      for( g : Gender )
        Population[ r, g ] = Births[ r ] - Deaths[ r, g ]
    Если интенсивность рождения Births одинакова и во всех регионах, то вы можете использовать в этом уравнении просто скалярную переменную Births:

Задание различных уравнений для различных наборов элементов массива

Однако, в некоторых случаях значения различных элементов массива могут вычисляться по разным формулам. Предположим, что в рассматриваемой нами модели населения люди интенсивнее мигрируют из северного региона (NORTH), в то время как миграция из южного региона (SOUTH) незначительна. В этом случае нужно задать два уравнения для различных элементов накопителя Population:

d(Population[Gender, SOUTH])/dt =Births[SOUTH] - Deaths[Gender, SOUTH]
d(Population[Gender, NORTH])/dt =Births[NORTH] - Deaths[Gender, NORTH] - OutMigration

Чтобы задать различные уравнения для различных подмассивов

  1. Выделите массив в графическом редакторе или в панели Проекты.
  2. Перейдите в панель Свойства.
  3. Задайте список индексов массива, идентифицирующий те элементы массива, значение которых будет вычисляться согласно заданной вами формуле. По умолчанию будут выбраны все размерности массива. Вы должны изменить этот список, указав конкретные элементы (или подразмерности) для тех размерностей, которые представлены в интересующем нас подмассиве лишь частично. Для таких размерностей, нужно будет щелкнуть мышью по ссылке с именем размерности и выбрать имя интересующего нас элемента или подразмерности из появившегося списка.
  4. Чтобы задать первую формулу в случае рассмотренного выше примера, щелкните по размерности и выберите элемент:

  5. Введите формулу, вычисляющую значение указанного подмассива, в поле d(<имя массива>)/dt = :

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

  8. Вы можете в любой момент удалить любую из этих формул, щелкнув мышью по кнопке справа от этой формулы.

Вы можете ссылаться в уравнениях и на определенные наборы элементов одной размерности с помощью подразмерностей. Например, если есть размерность Income = { POOR, MIDDLECLASS, WEALTHY } и ее подразмерность AllButPoor = { MIDDLECLASS, WEALTHY }, то вы можете написать для массива Population с тремя размерностями:

d( Population[ Region, Gender, POOR ] )/dt =
    Births[ Region ] - Deaths[ Region, Gender, POOR ] - OutMigration

d( Population[ Region, Gender, AllButPoor ] )/dt =
    Births[ Region ] - Deaths[ Region, Gender, AllButPoor ]

Ссылки на следующий, предыдущий и другие случайные элементы

Иногда вам может понадобиться сослаться в уравнении на какой-то другой индекс массива. Предположим, вы моделируете цепочку возрастов и ваш накопитель Population представляет собой массив с одной размерностью Age = { 0 .. 99 }. Для всех элементов массива (кроме элемента с индексом 0) значение входящего потока будет равняться значению выходящего потока из элемента с предыдущим индексом. Для элемента с индексом 0 входящий поток будет равен Births. Чтобы реализовать такой случай, вам нужно создать две подразмерности размерности Age: Age0 = { 0 } и AgesAllBut0 = { 1 .. 99 } и написать:


d( Population[ Age0 ] )/dt =
Births - Deaths[ Age0 ] - Ageing[ Age0 ]

d( Population[ AgesAllBut0 ] )/dt =
    Ageing[ AgesAllBut0 - 1 ] - Deaths[ AgesAllBut0 ] - Ageing[ AgesAllBut0 ]

Чтобы понять, как это работает, рассмотрим циклы, в которые AnyLogic будет преобразовывать эти формулы:


for( a : 0 }
    Population[ a ] = Births - Deaths[ a ] - Ageing[ a ]

for( a : 1..99 }
    Population[ a ] = Ageing[ a-1 ] - Deaths[ a ] - Ageing[ a ]

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

Функции массива элементов

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

Функции статистики

С помощью функций, перечисленных в приведенной ниже таблице, вы можете вычислять статистические характеристики массива.

Функция Описание
double average() Среднее элементов:

boolean hasNegativeValues() Проверяет, есть ли в массиве элементы с отрицательным значением. Возвращает true, если был найден хотя бы один такой элемент, иначе возвращает false.
double max() Максимальное значение.
double min() Минимальное значение.
double prod() Произведение элементов:

int size() Возвращает общее количество элементов в массиве.
double stddev() Среднеквадратичное отклонение:

double sum() Сумма элементов:

Вы можете получать статистические данные по определенным подмассивам массива. В этом случае, вам нужно перечислить с помощью аргументов соответствующей функции размерности, образующие этот подмассив. Например, пусть у вас есть массив people с размерностями ( Region, Gender, AgeGroup ), тогда вызов функции

people.sum( NORTH, INDEX_CAN_VARY, ADULT )

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

Функции массива, изменяющие значения элементов

Следующие функции позволяют изменять значения элементов определенного индекса в массиве.

int INDEX_CAN_VARY является константой, которая помещается на место индекса, и позволяет функциям, которые производят операции над размерностями массива, менять значение соответственного индекса. Другие индексы задаются как обычно.

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

Параметры:
indexes — индексы массива, константа INDEX_CAN_VARY для изменяющихся индексов
value — значение, которое нужно добавить или вычесть для значения элементов массива
factor — фактор, который применяется к массиву

Функция Описание
void increment(int... indexes) Увеличивает значения элементов (+1) заданного индекса.
void incrementBy(double value, int... indexes) Увеличивает значения элементов определенного индекса на заданное значение (+value).
void decrement(int... indexes) Уменьшает значения элементов (-1) заданного индекса.
void decrementBy(double value, int... indexes) Уменьшает значения элементов определенного индекса на заданное значение (-value).
void multiply(double factor, int... indexes) Умножает значения элементов определенного индекса на заданный фактор factor.
Как мы можем улучшить эту статью?