Блокноты MuPAD® будут демонтированы в будущем релизе. Используйте live скрипты MATLAB® вместо этого.
Live скрипты MATLAB поддерживают большую часть функциональности MuPAD, хотя существуют некоторые различия. Для получения дополнительной информации смотрите, Преобразовывают Notebook MuPAD в Live скрипты MATLAB.
Предположим данные, которые вы хотите собрать, сгенерирован поэлементно, и вы знаете заранее, сколько элементов будет сгенерировано. Интуитивный подход для сбора таких данных должен создать пустой список и добавить каждый новый элемент в конец списка. Например, эта процедура использует этот подход, чтобы собрать случайные целые числа, сгенерированные random
:
col := proc(n) local L, i; begin L := []; for i from 1 to n do L := L.[random()]; end_for; end:
Процедура генерирует случайные целые числа и собирает их в списке:
col(5)
Чтобы оценить производительность этого подхода, используйте процедуру col
, чтобы сгенерировать список 50 000 случайных чисел:
time(col(50000))
Функция time
возвращает результаты, измеренные в миллисекундах.
Теперь, проверяйте, сколько времени процедура на самом деле проводит генерирующиеся случайные числа:
time(random() $ i = 1..50000)
Таким образом процедура тратит наиболее часто добавление недавно сгенерированных чисел к списку. В MuPAD®, добавляя новую запись в список записей n
занимает время пропорциональный n
. Поэтому время выполнения col(n)
пропорционально n 2. Можно визуализировать эту зависимость путем графического вывода времен, которые col(n)
проводит при создании списков 1 - 50 000 записей:
plotfunc2d(n -> time(col(n)), n = 1..50000, Mesh = 20, AdaptiveMesh = 0)
При добавлении новой записи в список MuPAD выделяет место для нового, более длинного списка. Затем это копирует все записи старого списка плюс новая запись в этот новый список. Более быстрый подход должен создать целый список целиком, не добавляя каждую новую запись отдельно. Этот подход называется параллельным набором, потому что вы создаете и собираете данные одновременно. Используйте оператор последовательности $
, чтобы реализовать этот подход:
col := proc(n) local i; begin [random() $ i = 1..n]; end:
Эта процедура проводит большую часть своего времени, генерируя случайные числа:
time(col(50000))
Предположим, что вы знаете, сколько элементов вы сгенерируете, но вы не можете сгенерировать их целиком. В этом случае лучшая стратегия состоит в том, чтобы создать список необходимой длины, заполняющей его с некоторой константой, такой как 0 или NIL
. Затем можно заменить любую запись этого списка со сгенерированным значением. В этом случае вы не должны генерировать элементы в порядке, в котором вы хотите, чтобы они появились в списке.
Например, используйте эту процедуру, чтобы сгенерировать список первого n
числа Лукаса. Процедура создает список записей n
, где каждая запись 0. Затем это присваивает значения первым двум записям. Чтобы заменить все другие записи списка с числами Лукаса, процедура использует цикл for
:
lucas := proc(n) local L, i; begin L := [0 $ n]; L[1] := 1; L[2] := 3; for i from 3 to n do L[i] := L[i-1] + L[i-2]; end_for; L end:
Измерьтесь время должно было создать список 10 000 чисел Лукаса:
time(lucas(10000))
Если вы используете процедуру, которая создает пустой список и добавляет, каждый сгенерировал номер Лукаса к этому списку, то создание списка 10 000 чисел Лукаса берет намного дольше:
lucas := proc(n) local L, i; begin L := []; L :=L.[1]; L := L.[3]; for i from 3 to n do L := L.[L[i-1] + L[i-2]]; end_for; L end:
time(lucas(10000))
Если вы не можете предсказать число элементов, которое вы сгенерируете, но иметь разумный верхний предел этого номера, использовать эту стратегию:
Создайте список с количеством записей, равных или больше, чем верхний предел.
Сгенерируйте данные и заполните список.
Отбросьте неиспользованную часть списка.
Например, используйте следующую процедуру, чтобы создать список. Записи этого списка являются модульными квадратами номера a
(a 2 ультрасовременных n). Вы не можете предсказать количество записей в получившемся списке, потому что это зависит от параметров a
и n
. Тем не менее, вы видите, что в этом случае количество записей в списке не может превысить n
:
modPowers := proc(a, n) local L, i; begin L := [0 $ n]; L[1] := a; L[2] := a^2 mod n; i := 2; while L[i] <> a do L[i + 1] := a*L[i] mod n; i := i + 1; end_while; L := L[1..i - 1]; end:
Когда вы вызываете modPowers
для a = 3
и a = 2
, это создает два списка различных длин:
modPowers(3, 14); modPowers(2, 14)
Часто, вы не можете предсказать число элементов и не можете оценить верхний предел этого номера, прежде чем вы начнете генерировать фактические данные. Один способ иметь дело с этой проблемой состоит в том, чтобы выбрать некоторый верхний предел и использовать стратегию, описанную в Известном Максимальном Наборе Длины. Если тот предел достигнут, то:
Выберите больший предел.
Создайте новый список с числом элементов, соответствующим новому пределу.
Скопируйте существующие собранные данные в новый список.
Как правило, увеличение длины списка постоянным множителем приводит к лучшей производительности, чем увеличение его постоянным количеством записей:
rndUntil42 := proc() local L, i; begin i := 1; L := [random()]; while L[i] mod 42 <> 0 do if i = nops(L) then L := L.L; end_if; i := i+1; L[i] := random(); end_while; L[1..i]; end:
SEED := 123456789: rndUntil42()
SEED := 123456789: time(rndUntil42() $ i = 1..500)
Также, если вы не можете предсказать число элементов, которое необходимо будет собрать, затем использовать table
, который растет автоматически (хэш-таблица):
rndUntil42 := proc() local L, i, j; begin i := 1; L := table(1 = random()); while L[i] mod 42 <> 0 do i := i+1; L[i] := random(); end_while; [L[j] $ j=1..i]; end:
SEED := 123456789: time(rndUntil42() $ i = 1..500)
В данном примере использование таблицы немного быстрее. Если вы изменяете значение 42 на другое значение, использование списка может быть быстрее. В целом таблицы предпочтительны, когда вы собираете большие объемы данных. Выберите подход, который работает лучше всего на решение вашей проблемы.