Как найти объединение функций

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

Возьмем такой простой пример:

  • У нас есть две функции —

    и

  • Вместе они порождают функцию

  • Составной функцией будет считаться

Как видите в примере выше, функция

применяется к функции

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

Давайте посмотрим на математическое определение составной функции:

  • Пусть

    и

    — две функции

  • Тогда составная функция будет состоять из

    и

    — это обозначается как

  • Составная функция

    определяется как функция

  • Функция

    задается через

На рисунке ниже показано графическое представление составных функций:

400

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

и

не равны между собой.

Это можно очень хорошо понять на примере. Представим машину, которая сначала запекает торт, а затем украшает его глазурью. Будем рассматривать эти действия как функции:

  • Запекание — функция

  • Украшение — функция

Машина будет производить торт, используя

— сначала печь, затем украшать. Но если функции поменять местами

, то машина сначала украсит сырой торт, а сожжет его в печке вместе со всеми украшениями. Такая перестановка действий не сработает, поэтому нам нужны оба домена.

Теперь рассмотрим, как обозначаются составные функции и их области:

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

  • Домен:

    читается как «

    от

    от

    ». В композиции

    домен функции

    становится

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

  • Пример: Если

    и

    , то

    от

    от

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

от

от

, то в итоге получим

.

Let $F={f(n) | f:mathbb Ntomathbb N}$

I want to prove that for any $f,gin F$, there is always an $hin F$ that is different from $f$ and $g$, and is larger than both of them.

I believe that the proof of «$mathbb N$ is an infinite set so there will always be a larger function» but it just doesn’t feel like a strong proof. The other thing I had in mind is to union functions.

Since functions are actually relations, is it valid to declare a union of $f$ and $g$, and simply say that there will always be a larger function $h?$

Время на прочтение
5 мин

Количество просмотров 6.9K

В преддверии старта занятий в новом потоке группы «Разработчик С++» подготовили перевод интересного материала.


Большинство алгоритмов STL в C++ используют всего лишь одну функцию для выполнения некоторой работы над коллекцией. Например, чтобы извлечь все четные числа из коллекции, мы можем написать такой код:

auto const numbers = std::vector<int>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto results = std::vector<int>{};

std::copy_if(begin(numbers), end(numbers), back_inserter(results), isMultipleOf2);

Предполагая, что у нас есть функция isMultipleOf2:

bool isMultipleOf2(int n)
{
    return (n % 2) == 0;
}

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

Но C++ не поддерживает комбинации функций. Например, если у нас также есть функция isMultipleOf3 и мы хотим извлечь числа, кратные 2 или кратные 3, было бы довольно просто написать такой код:

std::copy_if(begin(numbers), end(numbers), back_inserter(results), isMultipleOf2 || isMultipleOf3);

Но он не компилируется: в C++ не существует такой вещи как оператор || для функций.

Самый простой способ, который предлагает стандарт C++ (начиная с C++11), — использовать лямбду:

std::copy_if(begin(numbers), end(numbers), back_inserter(results), [](int number){ return isMultipleOf2(number) || isMultipleOf3(number); });

Этот код компилируется и извлекает из коллекции числа, кратные 2 или 3.

Но так код приобретает нежелательный излишек:

синтаксис лямбды: скобки [], список параметров, скобки {...} и т. д.

параметр: number.

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

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

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

Решение № 1: разработка функции объединения

Я не думаю, что есть способ написать оператор || для функций в общем случае, так чтобы можно было написать isMultipleOf2 || isMultipleOf3. Действительно, функции в общем смысле включают лямбды, и лямбды могут быть любого типа. Таким образом, такой оператор будет оператором || для всех типов. Это было бы слишком навязчиво для остальной части кода.

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

Было бы разумно поместить такое общее имя в пространство имен, чтобы избежать коллизий. Но в целях примера давайте просто назовем ее or_. Целевое использование or_ будет следующим:

std::copy_if(begin(numbers), end(numbers), back_inserter(results), or_(isMultipleOf2, isMultipleOf3));

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



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

template<typename Function1, typename Function2>
auto or_(Function1 function1, Function2 function2)
{
    return [function1, function2](auto const& value){ return function1(value) || function2(value); };
}

Мы сделали выбор, принять параметр лямбда по const&. Это связано с тем, что в алгоритмах STL без сохранения состояния — без стресса, а это означает, что все проще, когда функциональные объекты не имеют побочных эффектов в алгоритмах STL, в частности предикатов, как у нас здесь.

Решение № 2: оператор || для определенного типа

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

Мы можем обойти это ограничение, зафиксировав тип:

template<typename Function>
struct func
{
   explicit func(Function function) : function_(function){}
   Function function_;
};

Затем мы можем определить оператор || для этого типа, и он не будет конфликтовать с другими типами в коде:

template<typename Function1, typename Function2>
auto operator||(func<Function1> function1, Function2 function2)
{
    return [function1, function2](auto const& value){ return function1.function_(value) || function2(value); };
}

Полученный код имеет преимущество в том, что || есть в его синтаксисе, но недостаток в конструкции func:

std::copy_if(begin(numbers), end(numbers), back_inserter(results), func(isMultiple(2)) || isMultiple(3));

Возможно, мы сможем найти более подходящее название для func, но если у вас есть предложения, пожалуйста, оставьте комментарий ниже.

Решение № 3: Использование Boost Phoenix

Цель библиотеки Boost Phoenix — написать объект сложной функции с простым кодом! Если вы не знакомы с Boost Phoenix, вы можете ознакомится с введением в Boost Phoenix, чтобы увидеть код, который она позволяет писать.

Boost Phoenix, хотя и впечатляющая библиотека, не может творить чудеса и не компилирует наш начальный целевой код (isMultipleOf2 || isMultipleOf3). Но она позволяет использовать создание объектов из isMultipleOf2 и isMultipleOf3, которые будут совместимы с остальной частью библиотеки.

Boost Phoenix вообще не использует макросы, но для этого конкретного случая:

BOOST_PHOENIX_ADAPT_FUNCTION(bool, IsMultipleOf2, isMultipleOf2, 1)
BOOST_PHOENIX_ADAPT_FUNCTION(bool, IsMultipleOf3, isMultipleOf3, 1)

Первая строка создает IsMultipleOf2 из isMultipleOf2, и мы должны указать, что isMultipleOf2 возвращает bool и принимает 1 параметр.

Затем мы можем использовать их таким образом (пример с полным кодом, чтобы показать #include):

#include <boost/phoenix/phoenix.hpp>
#include <vector>
 
bool isMultipleOf2(int n)
{
    return (n % 2) == 0;
}
 
bool isMultipleOf3(int n)
{
    return (n % 3) == 0;
}
 
BOOST_PHOENIX_ADAPT_FUNCTION(bool, IsMultipleOf2, isMultipleOf2, 1)
BOOST_PHOENIX_ADAPT_FUNCTION(bool, IsMultipleOf3, isMultipleOf3, 1)
 
int main()
{
    auto const numbers = std::vector<int>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    auto results = std::vector<int>{};
 
    using boost::phoenix::arg_names::arg1;
    std::copy_if(begin(numbers), end(numbers), back_inserter(results), IsMultipleOf2(arg1) || IsMultipleOf3(arg1));
}

Ценой за хороший синтаксис — использование ||, это появление arg1, что означает первый аргумент, переданный этим функциям. В нашем случае объекты, успешно переданные этой функции, являются элементами внутри чисел коллекций.

Итак, что вы думаете об этих методах объединения нескольких функций логическими операциями? Видите ли вы другие способы написать это более выразительным кодом?

Бесплатный вебинар: «Контейнеры STL на все случаи жизни»

Термин сложная функция в действительности в математическом языке является «чисто рабочим»: так называют функцию, если она задана в виде у=f(g(x)) с внешней функцией f и внутренней функцией g. Из самого задания этой функции ясно, что для вычисления значения у сложной функции к значению аргумента х сначала применяется функция g, а затем к полученному значению g(x) применяется функция f — тогда и получается значение f(g(x)). Сложная функция (композиция функций)

Владение этим термином, умение видеть сложную функцию для начал математического анализа исключительно — чтобы найти производную, функцию часто следует представить в виде сложной функции, причем функция может быть еще более «сложной», когда ее «история» более длинная, т.е., например, если функция задается формулой у=f(g(h(р(х))).

Для того чтобы подчеркнуть, что термин «сложная функция» относится не к самой функции, а к способу ее задания, приведем пример: функции $y=sqrt[3]{x^3}$ и $y=x$ — это, очевидно, одна и та же функция, однако первую из них можно назвать сложной, а вторую — нет. Заметим также, что сложная функция может оказаться нигде не определенной, например,$y=sqrt{-x^2-1}$ — под знаком радикала тут всегда стоит отрицательное выражение.

При желании заняться алгеброй функций, т.е. рассматривать операции, действия, которые можно осуществлять с функциями, изучать свойства этих операций, а иногда лишь для терминологического удобства сложную функцию у=f(g(x)) называют композицией функций f и g и обозначают обычно символом $fcirc g$ или, в обратном порядке, $gcirc f$ — математики, как ни странно, не могут, да и не пытаются, прийти к общему соглашению относительно этого обозначения. Далее мы применяем первый порядок f и g, т.е. $(f circ g)(x)=f(g(x))$.

А между тем композиция двух функций зависит от их порядка: если, например, $f(x)=x^2$, $g(x)=sqrt {x}$, то $f(g(x))=(sqrt{x})^2=x, (xgeq 0)$ тогда как $g(f(x))=sqrt{x^2}=|x|$, а значит, это две различные функции — они имеют даже разные области определения. Иными словами, равенство $fcirc g=gcirc f$ выполняется не для всех функций, так что в алгебре функций перестановочный (в математике, в отличие от школы, называют его коммутативным) закон для композиции не имеет места.

Интересно, что сочетательный (в математике говорят ассоциативный) закон остается в силе:

$[(fcirc g)circ h](x)=(fcirc g)(h(x))=f(g(h(x)))$,

$[fcirc(gcirc h)](x)=f[(gcirc h)(x)]=f(g(h(x)))$

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

$fcirc(g+h)=(fcirc g)+ (fcirc h)$ и $(g+h)circ f=(gcirc f)+(hcirc f)$

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

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

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

Материалы по теме:

  • Периодические функции
  • Возрастающие и убывающие функции
  • Крайние случаи в математике
  • Функции четные и нечетные

Загрузка…

Иногда
у аналитика возникают сомнения
относительно блоков диаграммы. На
хорошей SADT-диаграмме блоки должны
обладать некоторыми важными качествами:

  • выполнять
    строго определенные функции;

  • иметь
    одинаковую сложность;

  • иметь
    одинаковый уровень детализации;

  • просто
    соединяться с другими блоками диаграммы;

  • воздействовать
    на управления, входы и выходы с
    определенным смыслом;

  • работать
    вместе с другими блоками для выполнения
    функции диаграммы.

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

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

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

10.3.2. Альтернативное объединение и разъединение дуг

Иногда
можно обнаружить две дуги, которые
начинаются и кончаются в одних и тех же
местах диаграммы. То есть обе дуги
начинаются и кончаются у одних и тех же
блоков (см. рис. 10-2). В этом случае
посмотрите на эти две дуги внимательно.
Может оказаться, что их следует объединить
в одну. Если вы можете придумать хорошее
наименование, объединяющее названия
этих дуг, объедините их. Если наличие
двух дуг имеет определенный смысл, не
объединяйте их. Объединение скрывает
детали, поэтому не делайте это механически.
Исчезновение деталей повредит диаграмме.
Например, замечания 7 и 8 на рис. 10-1
отражают попытку объединить брак
и сырье,
отвергнутую из-за того, что они оказались
различными вещами.

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

и незаконченное
задание

не были объединены в одну выходную дугу.
Они отражают различные типы данных
(первая — понятие, вторая — физическую
реальность) и каждая влияет на свою
часть родительской диаграммы {управлять
выполнением, задания

и контролировать
качество выполнения

соответственно). Поэтому их раздельное
изображение дает более ясную картину
результатов работы блока определить
степень выполнения задания.


Рис.
10-3. Пересмотренная диаграмма

Соседние файлы в папке ИТ в Интернет

  • #
  • #
  • #
  • #
  • #
  • #

Понравилась статья? Поделить с друзьями:
  • Как найти диаметр паропровода
  • Как найти в реестре регистрационные данные
  • Как правильно составить видеовизитку
  • Как найти вульгрим в darksiders 2
  • Как составить проект газификации