Как создать кусочно-встроенную функцию в MATLAB? - PullRequest
7 голосов
/ 28 апреля 2009

У меня есть функция в MATLAB, которая принимает другую функцию в качестве аргумента. Я хотел бы как-то определить кусочно-встроенную функцию, которую можно передать. Это как-то возможно в MATLAB?

Редактировать: функцию, которую я хотел бы представить:

f(x) = { 1.0,  0.0 <= x <= 0.5,
         -1.0, 0.5 < x <= 1.0

where 0.0 <= x <= 1.0

Ответы [ 4 ]

12 голосов
/ 28 апреля 2009

Вы действительно определили кусочную функцию с тремя точками разрыва, то есть в [0, 0,5, 1]. Однако вы не определили значение функции за пределами перерывов. (Кстати, я использовал здесь термин «разрыв», потому что мы действительно определяем простую форму сплайна, кусочно-постоянный сплайн. Я мог бы также использовать термин «узел», еще одно распространенное слово в мире сплайнов. )

Если вы точно знаете, что никогда не будете оценивать функцию за пределами [0,1], тогда проблем не будет. Так что тогда просто определите кусочную функцию с ОДНОЙ точкой разрыва, при х = 0,5. Простой способ определить кусочно-постоянную функцию, подобную вашей, состоит в использовании логического оператора. Таким образом, тест (x> 0,5) возвращает константу, либо 0, либо 1. Путем масштабирования и перевода этого результата легко создать функцию, которая делает то, что вы хотите.

constfun = @(x) (x > 0.5)*2 - 1;

Встроенная функция делает то же самое, но встроенные функции ОЧЕНЬ медленны по сравнению с анонимной функцией. Я настоятельно рекомендую использовать анонимную форму. В качестве теста попробуйте это:

infun = inline('(x > 0.5)*2 - 1','x');
x = 0:.001:1;

tic,y = constfun(x);toc
Elapsed time is 0.002192 seconds.

tic,y = infun(x);toc
Elapsed time is 0.136311 seconds.

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

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

-1 when x < 0,
2 when 0 <= x < 1,
1 when 1 <= x

Одним из решений является использование функции Хевисайда . Итак, сначала определим базовую единицу функции Хевисайда.

H = @(x) (x >= 0);

Наша кусочная функция теперь получена из H (x).

P = @(x) -1 + H(x)*3 + H(x-1)*(-1);

Смотрите, что есть три части к P (x). Первый член - это то, что происходит для x ниже первой точки разрыва. Затем мы добавляем кусок, который вступает в силу выше нуля. Наконец, третья часть добавляет еще одно смещение выше x == 1. Это достаточно легко вычерчивается.

ezplot(P,[-3,3])

Более сложные сплайны легко генерируются с этого начала. Так что я назвал эту конструкцию снова сплайном. На самом деле, это то, к чему мы можем привести. На самом деле, это то, к чему это ведет. Сплайн является кусочной функцией, тщательно связанной между собой в виде списка узлов или точек разрыва. В частности, сплайны часто имеют заданные порядки непрерывности, поэтому, например, кубический сплайн будет дважды дифференцируемым (C2) через разрывы. Существуют также кусочно-кубические функции, которые являются только функциями C1. Моя точка зрения на все это заключается в том, что я описал простую начальную точку для формирования любой кусочной функции. Он работает довольно хорошо для полиномиальных сплайнов, хотя для выбора коэффициентов этих функций может потребоваться небольшая математика.

Еще один способ создания этой функции - явный кусочно-полином. В MATLAB у нас есть малоизвестная функция mkpp. Попробуйте это ...

pp = mkpp([0 .5 1],[1;-1]);

Если бы у вас был набор инструментов сплайнов, fnplt построит это непосредственно для вас. Предполагая, что у вас нет этого туберкулеза, сделайте следующее:

ppfun = @(x) ppval(pp,x);
ezplot(ppfun,[0 1])

Оглядываясь назад на вызов mkpp, он все-таки довольно прост. Первый аргумент - это список точек разрыва на кривой (в виде вектора ROW). Второй аргумент - это вектор COLUMN с кусочно-постоянными значениями, которые кривая примет в этих двух определенных интервалах между перерывами.

Несколько лет назад я опубликовал еще одну опцию, piecewise_eval . Его можно скачать с центрального обмена файлами MATLAB. Это функция, которая позволит пользователю задавать кусочную функцию исключительно в виде списка точек разрыва вместе с функциональными частями между этими перерывами. Таким образом, для функции с одним разрывом при х = 0,5 мы бы сделали это:

fun = @(x) piecewise_eval(x,0.5,{1,-1});

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

fun = @(x) piecewise_eval(x,[0 0.5 1],{NaN,1,-1,NaN});

Моя цель во всей этой довольно продолжительной экскурсии - понять, что такое кусочная функция, и несколько способов ее создания в MATLAB.

5 голосов
/ 28 апреля 2009

К сожалению, в MATLAB нет тернарного оператора, который бы упростил подобные вещи, но если немного расширить подход gnovice, вы можете создать анонимную функцию, например:

fh = @(x) ( 2 .* ( x <= 0.5 ) - 1 )

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

4 голосов
/ 28 апреля 2009

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

f = inline('2.*(x <= 0.5)-1');

Однако, как указано в других ответах, анонимные функции используются чаще и эффективнее:

f = @(x) (2.*(x <= 0.5)-1);
2 голосов
/ 06 ноября 2013

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

when x<0    : x^2 + 3x
when 0<=x<=4: e^x
when x>4    : log(x)

Сначала я бы определил логические маски для каждой кусочной области:

PIECE1 = @(x) x<0
PIECE2 = @(x) x>=0 & x<=4
PIECE3 = @(x) x>4

Тогда я бы сложил их все вместе:

f = @(x) PIECE1(x).*(x.^2+3*x) + PIECE2(x).*exp(x) + PIECE3(x).*log(x)

x = -10:.1:10
figure;
plot(x,f(x))
...