<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Joinmek</id>
	<title>Алговики - Вклад [ru]</title>
	<link rel="self" type="application/atom+xml" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Joinmek"/>
	<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/ru/%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/Joinmek"/>
	<updated>2026-10-23T03:20:38Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21529</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21529"/>
		<updated>2017-01-06T22:39:49Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма. Но если наложить ограничение на минимально допустимую величину отрезка, как было сделано ранее, то его можно оценить следующим образом: O((B - A) / (G * p)), где p -количество процессов. Каждый процесс считает интеграл на своем отрезке вне зависимости от других процессов, влияние других процессоров происходит исключительно в момент ожидания получения доступа к глобальному стеку.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл с помощью метода глобального стека на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности) эффективность остается примерно на том же уровне.&lt;br /&gt;
Данная реализация не отражает в полной мере масштабируемость алгоритма, т. к. остается проблема неравномерного распределения задач по процессорам.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости алгоритма на системе с общей памятью, реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] была запущена на архитектуре IBM pSeries 690 HPC Regatta. Программа запускалась на 1, 2, 4, 8 и 16 потоках. Точность так же, как и в первом случае, градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута 2 потоках и равна 0.9504; минимальная - на 16, ее величина - 0.67484. При увеличении количества потоков эффективность падает из-за конкуренции доступа к глобальному стеку. При увеличении сложности для одинакового числа процессов эффективность остается примерно на том же уровне.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа потоков и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21461</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21461"/>
		<updated>2016-12-20T02:13:52Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма. Но если наложить ограничение на минимально допустимую величину отрезка, как было сделано ранее, то его можно оценить следующим образом: O((B - A) / (G * p)), где p -количество процессов. Каждый процесс считает интеграл на своем отрезке вне зависимости от других процессов, влияние других процессоров происходит исключительно в момент ожидания получения доступа к глобальному стеку.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл с помощью метода глобального стека на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности) эффективность остается примерно на том же уровне.&lt;br /&gt;
Данная реализация не отражает в полной мере масштабируемость алгоритма, т. к. остается проблема неравномерного распределения задач по процессорам.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости алгоритма на системе с общей памятью, реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] была запущена на архитектуре IBM pSeries 690 HPC Regatta. Программа запускалась на 1, 2, 4, 8 и 16 потоках. Точность так же, как и в первом случае, градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута 2 потоках и равна 0.9504; минимальная - на 16, ее величина - 0.67484. При увеличении количества потоков эффективность падает из-за конкуренции доступа к глобальному стеку. При увеличении сложности для одинакового числа процессов эффективность остается примерно на том же уровне.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа потоков и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21460</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21460"/>
		<updated>2016-12-20T02:13:17Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Ресурс параллелизма алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма. Но если наложить ограничение на минимально допустимую величину отрезка, как было сделано ранее, то его можно оценить следующим образом: O((B - A) / (G * p)), где p -количество процессов. Каждый процесс считает интеграл на своем отрезке вне зависимости от других процессов, влияние других процессоров происходит исключительно в момент ожидания получения доступа к глобальному стеку.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл с помощью метода глобального стека на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности) эффективность остается примерно на том же уровне.&lt;br /&gt;
Данная реализация не отражает в полной мере масштабируемость алгоритма, т. к. остается проблема неравномерного распределения задач по процессорам.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости алгоритма на системе с общей памятью, реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] была запущена на архитектуре IBM pSeries 690 HPC Regatta. Программа запускалась на 1, 2, 4, 8 и 16 потоках. Точность так же, как и в первом случае, градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута 2 потоках и равна 0.9504; минимальная - на 16, ее величина - 0.67484. При увеличении количества потоков эффективность падает из-за конкуренции доступа к глобальному стеку. При увеличении сложности для одинакового числа процессов эффективность остается примерно на том же уровне.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа потоков и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21459</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21459"/>
		<updated>2016-12-20T02:10:21Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Ресурс параллелизма алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма. Но если наложить ограничение на минимально допустимую величину отрезка, как было сделано ранее, то его можно оценить следующим образом: O((B - A) / (G * p)), где p -количество процессов. Каждый процесс считает интеграл на своем отрезке вне зависимости от других процессов, взаимодействие происходит исключительно в момент ожидания получения доступа к глобальному стеку.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл с помощью метода глобального стека на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности) эффективность остается примерно на том же уровне.&lt;br /&gt;
Данная реализация не отражает в полной мере масштабируемость алгоритма, т. к. остается проблема неравномерного распределения задач по процессорам.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости алгоритма на системе с общей памятью, реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] была запущена на архитектуре IBM pSeries 690 HPC Regatta. Программа запускалась на 1, 2, 4, 8 и 16 потоках. Точность так же, как и в первом случае, градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута 2 потоках и равна 0.9504; минимальная - на 16, ее величина - 0.67484. При увеличении количества потоков эффективность падает из-за конкуренции доступа к глобальному стеку. При увеличении сложности для одинакового числа процессов эффективность остается примерно на том же уровне.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа потоков и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21458</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21458"/>
		<updated>2016-12-20T02:10:20Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Ресурс параллелизма алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма. Но если наложить ограничение на минимально допустимую величину отрезка, как было сделано ранее, то его можно оценить следующим образом: O((B - A) / (G * p)), где p -количество процессов. Каждый процесс считает интеграл на своем отрезке вне зависимости от других процессов, взаимодействие происходит исключительно в момент ожидания получения доступа к глобальному стеку.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл с помощью метода глобального стека на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности) эффективность остается примерно на том же уровне.&lt;br /&gt;
Данная реализация не отражает в полной мере масштабируемость алгоритма, т. к. остается проблема неравномерного распределения задач по процессорам.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости алгоритма на системе с общей памятью, реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] была запущена на архитектуре IBM pSeries 690 HPC Regatta. Программа запускалась на 1, 2, 4, 8 и 16 потоках. Точность так же, как и в первом случае, градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута 2 потоках и равна 0.9504; минимальная - на 16, ее величина - 0.67484. При увеличении количества потоков эффективность падает из-за конкуренции доступа к глобальному стеку. При увеличении сложности для одинакового числа процессов эффективность остается примерно на том же уровне.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа потоков и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21457</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21457"/>
		<updated>2016-12-20T02:00:30Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл с помощью метода глобального стека на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности) эффективность остается примерно на том же уровне.&lt;br /&gt;
Данная реализация не отражает в полной мере масштабируемость алгоритма, т. к. остается проблема неравномерного распределения задач по процессорам.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости алгоритма на системе с общей памятью, реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] была запущена на архитектуре IBM pSeries 690 HPC Regatta. Программа запускалась на 1, 2, 4, 8 и 16 потоках. Точность так же, как и в первом случае, градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута 2 потоках и равна 0.9504; минимальная - на 16, ее величина - 0.67484. При увеличении количества потоков эффективность падает из-за конкуренции доступа к глобальному стеку. При увеличении сложности для одинакового числа процессов эффективность остается примерно на том же уровне.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа потоков и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21456</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21456"/>
		<updated>2016-12-20T02:00:29Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл с помощью метода глобального стека на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности) эффективность остается примерно на том же уровне.&lt;br /&gt;
Данная реализация не отражает в полной мере масштабируемость алгоритма, т. к. остается проблема неравномерного распределения задач по процессорам.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости алгоритма на системе с общей памятью, реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] была запущена на архитектуре IBM pSeries 690 HPC Regatta. Программа запускалась на 1, 2, 4, 8 и 16 потоках. Точность так же, как и в первом случае, градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута 2 потоках и равна 0.9504; минимальная - на 16, ее величина - 0.67484. При увеличении количества потоков эффективность падает из-за конкуренции доступа к глобальному стеку. При увеличении сложности для одинакового числа процессов эффективность остается примерно на том же уровне.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа потоков и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21455</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21455"/>
		<updated>2016-12-20T02:00:28Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл с помощью метода глобального стека на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности) эффективность остается примерно на том же уровне.&lt;br /&gt;
Данная реализация не отражает в полной мере масштабируемость алгоритма, т. к. остается проблема неравномерного распределения задач по процессорам.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости алгоритма на системе с общей памятью, реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] была запущена на архитектуре IBM pSeries 690 HPC Regatta. Программа запускалась на 1, 2, 4, 8 и 16 потоках. Точность так же, как и в первом случае, градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута 2 потоках и равна 0.9504; минимальная - на 16, ее величина - 0.67484. При увеличении количества потоков эффективность падает из-за конкуренции доступа к глобальному стеку. При увеличении сложности для одинакового числа процессов эффективность остается примерно на том же уровне.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа потоков и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21454</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21454"/>
		<updated>2016-12-20T01:58:37Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл с помощью метода глобального стека на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
Данная реализация не отражает в полной мере масштабируемость алгоритма, т. к. остается проблема неравномерного распределения задач по процессорам.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости алгоритма на системе с общей памятью, реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] была запущена на архитектуре IBM pSeries 690 HPC Regatta. Программа запускалась на 1, 2, 4, 8 и 16 потоках. Точность так же, как и в первом случае, градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута 2 потоках и равна 0.9504; минимальная - на 16, ее величина - 0.67484. При увеличении количества потоков эффективность падает из-за конкуренции доступа к глобальному стеку. При увеличении сложности для одинакового числа процессов эффективность остается примерно на том же уровне.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа потоков и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21453</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21453"/>
		<updated>2016-12-20T01:58:36Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл с помощью метода глобального стека на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
Данная реализация не отражает в полной мере масштабируемость алгоритма, т. к. остается проблема неравномерного распределения задач по процессорам.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости алгоритма на системе с общей памятью, реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] была запущена на архитектуре IBM pSeries 690 HPC Regatta. Программа запускалась на 1, 2, 4, 8 и 16 потоках. Точность так же, как и в первом случае, градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута 2 потоках и равна 0.9504; минимальная - на 16, ее величина - 0.67484. При увеличении количества потоков эффективность падает из-за конкуренции доступа к глобальному стеку. При увеличении сложности для одинакового числа процессов эффективность остается примерно на том же уровне.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа потоков и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21452</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21452"/>
		<updated>2016-12-20T01:49:17Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл с помощью метода глобального стека на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
Данная реализация не отражает в полной мере масштабируемость алгоритма, т. к. остается проблема неравномерного распределения задач по процессорам.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости алгоритма на системе с общей памятью, реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] была запущена на архитектуре IBM pSeries 690 HPC Regatta. Программа запускалась на 1, 2, 4, 8 и 16 потоках. Точность так же, как и в первом случае, градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута 2 потоках и равна 0.9504; минимальная - на 16, ее величина - 0.67484. При увеличении количества потоков эффективность падает из-за конкуренции доступа к глобальному стеку. При увеличении сложности для одинакового числа процессов эффективность медленно падает.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа потоков и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21451</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21451"/>
		<updated>2016-12-20T01:48:18Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл с помощью метода глобального стека на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
Данная реализация не отражает в полной мере масштабируемость алгоритма, т. к. остается проблема неравномерного распределения задач по процессорам.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости алгоритма, реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] была запущена на архитектуре IBM pSeries 690 HPC Regatta. Программа запускалась на 1, 2, 4, 8 и 16 потоках. Точность так же, как и в первом случае, градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута 2 потоках и равна 0.9504; минимальная - на 16, ее величина - 0.67484. При увеличении количества потоков эффективность падает из-за конкуренции доступа к глобальному стеку. При увеличении сложности для одинакового числа процессов эффективность медленно падает.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа потоков и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21450</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21450"/>
		<updated>2016-12-20T01:43:08Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
Для оценки эффективности алгоритма, реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] была запущена на архитектуре IBM pSeries 690 HPC Regatta. Программа запускалась на 1, 2, 4, 8 и 16 потоках. Точность так же, как и в первом случае, градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута 2 потоках и равна 0.9504; минимальная - на 16, ее величина - 0.67484. При увеличении количества потоков эффективность падает из-за конкуренции доступа к глобальному стеку. При увеличении сложности для одинакового числа процессов эффективность медленно падает.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа потоков и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21449</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21449"/>
		<updated>2016-12-20T01:41:10Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
Для оценки эффективности алгоритма, реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] была запущена на архитектуре IBM pSeries 690 HPC Regatta. Программа запускалась на 1, 2, 4, 8 и 16 потоках. Точность так же, как и в первом случае, градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута 2 потоках и равна 0.9504; минимальная - на 16, ее величина - 0.67484. При увеличении количества потоков эффективность падает из-за конкуренции доступа к глобальному стеку. При увеличении сложности для одинакового числа процессов эффективность медленно падает.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21448</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21448"/>
		<updated>2016-12-20T01:41:09Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
Для оценки эффективности алгоритма, реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] была запущена на архитектуре IBM pSeries 690 HPC Regatta. Программа запускалась на 1, 2, 4, 8 и 16 потоках. Точность так же, как и в первом случае, градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута 2 потоках и равна 0.9504; минимальная - на 16, ее величина - 0.67484. При увеличении количества потоков эффективность падает из-за конкуренции доступа к глобальному стеку. При увеличении сложности для одинакового числа процессов эффективность медленно падает.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21447</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21447"/>
		<updated>2016-12-20T01:27:50Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21446</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21446"/>
		<updated>2016-12-20T01:24:57Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_time_regatta.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
[[Файл:VM_efficiency_regatta.png|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21432</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21432"/>
		<updated>2016-12-19T20:17:48Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Схема реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21431</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21431"/>
		<updated>2016-12-19T20:17:47Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Схема реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sAB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21430</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21430"/>
		<updated>2016-12-19T20:17:08Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Схема реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| &amp;lt;= ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21429</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21429"/>
		<updated>2016-12-19T20:17:07Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Схема реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| &amp;lt;= ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21423</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21423"/>
		<updated>2016-12-19T19:42:45Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
&lt;br /&gt;
Чтобы продемонстрировать работу алгоритма на архитектуре BlueGene, было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpixlcxx_r с опцией -O2. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21188</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21188"/>
		<updated>2016-12-18T15:24:26Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Свойства алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма на основе общей памяти не требуется управляющий процесс. На системах с распределенной памятью отдельный управляющий процесс также не нужен.  Распределение отрезков и финальное сложение можно провести на нулевом процессе.&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным. На это свойство влияет выполнение свойства ассоциативности сложения, но не влияют свойства интегрируемой функции.&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым не для всех функций. Например, для sinx алгоритм неустойчив. Можно рассмотреть отрезки интегрирования [0, 2*pi] и [0, 2*pi + delta]. В первом случае, алгоритм остановится на первой итерации, и значение интеграла будет равно 0. При изменении правой границы отрезка на малое delta, алгоритм не остановится на первой итерации, и значение интеграла будет сильно отличаться от 0.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, на архитектуре Blue Gene, на которой она тестировалась, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpicc версии 4.1.2 с опцией -lm. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21182</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21182"/>
		<updated>2016-12-18T14:41:56Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, на архитектуре Blue Gene, на которой она тестировалась, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpicc версии 4.1.2 с опцией -lm. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21181</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21181"/>
		<updated>2016-12-18T14:41:55Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке. Такой вариант проще реализовать, но его недостаток в том, что увеличивается количество обращений процессов к глобальному стеку, и каждый процесс должен ждать своей очереди, что может привести к увеличению времени работы программы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, на архитектуре Blue Gene, на которой она тестировалась, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpicc версии 4.1.2 с опцией -lm. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21180</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21180"/>
		<updated>2016-12-18T14:30:26Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Последовательная сложность алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
Если предположить, что алгоритм будет останавливаться при достижении длины отрезка меньше некоторого порога G, то сложность можно оценить как O((B - A) / G).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, на архитектуре Blue Gene, на которой она тестировалась, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpicc версии 4.1.2 с опцией -lm. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21178</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=21178"/>
		<updated>2016-12-18T14:00:42Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Общее описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение интеграла от новой (упрощенной) функции, после чего значения интегралов суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, на архитектуре Blue Gene, на которой она тестировалась, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpicc версии 4.1.2 с опцией -lm. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Joinmek&amp;diff=18970</id>
		<title>Обсуждение участника:Joinmek</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Joinmek&amp;diff=18970"/>
		<updated>2016-11-22T16:31:06Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Статья [[Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки (2)]] ==&lt;br /&gt;
&lt;br /&gt;
* Нет краткой сводки свойств алгоритма в начале страницы (Исправлено)&lt;br /&gt;
* Нумерация подразделов не соответствует шаблону (Исправлено)&lt;br /&gt;
* Разделы 1.6 и 1.8 (6 и 8 в оригинальной статье) не заполнены (Исправлено)&lt;br /&gt;
* Картинка в разделе 1.7 (7) не является информационным графом (Исправлено)&lt;br /&gt;
** Если это информационный граф, из него следует, что алгоритм строго последовательный. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:29, 21 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
** Приведённый граф очень мелкий, надписи практически нечитаемы. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:29, 21 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* Раздел 2.7 отсутствует (Исправлено)&lt;br /&gt;
* Список литературы отсутствует (Исправлено)&lt;br /&gt;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:19, 1 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:19, 1 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:03, 16 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* Рисунки в статье не подписаны и не пронумерованы. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:29, 21 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:29, 21 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:29, 21 ноября 2016 (MSK) (Исправлено)&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18969</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18969"/>
		<updated>2016-11-22T16:30:14Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{assignment}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, на архитектуре Blue Gene, на которой она тестировалась, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpicc версии 4.1.2 с опцией -lm. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18968</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18968"/>
		<updated>2016-11-22T16:22:48Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, на архитектуре Blue Gene, на которой она тестировалась, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpicc версии 4.1.2 с опцией -lm. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках, а также библиотека OpenMPI версии 1.68.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18967</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18967"/>
		<updated>2016-11-22T16:21:42Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Схема реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, на архитектуре Blue Gene, на которой она тестировалась, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpicc версии 4.1.2 с опцией -lm. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18966</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18966"/>
		<updated>2016-11-22T16:21:18Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Схема реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    --&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, на архитектуре Blue Gene, на которой она тестировалась, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpicc версии 4.1.2 с опцией -lm. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18965</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18965"/>
		<updated>2016-11-22T16:20:47Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Схема реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, на архитектуре Blue Gene, на которой она тестировалась, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpicc версии 4.1.2 с опцией -lm. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18964</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18964"/>
		<updated>2016-11-22T16:14:03Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, на архитектуре Blue Gene, на которой она тестировалась, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpicc версии 4.1.2 с опцией -lm. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18963</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18963"/>
		<updated>2016-11-22T16:11:57Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpicc версии 4.1.2 с опцией -lm. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18962</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18962"/>
		<updated>2016-11-22T16:11:56Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpicc версии 4.1.2 с опцией -lm. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.3. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.4. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18961</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18961"/>
		<updated>2016-11-22T16:10:14Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene]. Использовался компилятор mpicc версии 4.1.2 с опцией -lm. Внутри программы использовалась стандартная библиотека языка C - math, которая была выбрана для подсчета функции синуса в заданных точках.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.2. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.3. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Масштабируемость:&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
При увеличении числа процессов эффективность программы падает, т. к. увеличиваются накладные расходы, а также по причине того, что некоторым процессам попадаются более трудоемкие участки интегрирования, из-за которых остальным процессам приходится ожидать.&lt;br /&gt;
&lt;br /&gt;
При фиксированном количестве процессов и увеличении сложности задачи (уменьшении точности), эффективность падает, но очень медленно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Joinmek&amp;diff=18960</id>
		<title>Обсуждение участника:Joinmek</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Joinmek&amp;diff=18960"/>
		<updated>2016-11-22T14:19:51Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Статья [[Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки (2)]] ==&lt;br /&gt;
&lt;br /&gt;
* Нет краткой сводки свойств алгоритма в начале страницы (Исправлено)&lt;br /&gt;
* Нумерация подразделов не соответствует шаблону (Исправлено)&lt;br /&gt;
* Разделы 1.6 и 1.8 (6 и 8 в оригинальной статье) не заполнены (Исправлено)&lt;br /&gt;
* Картинка в разделе 1.7 (7) не является информационным графом (Исправлено)&lt;br /&gt;
** Если это информационный граф, из него следует, что алгоритм строго последовательный. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:29, 21 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
** Приведённый граф очень мелкий, надписи практически нечитаемы. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:29, 21 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* Раздел 2.7 отсутствует (Исправлено)&lt;br /&gt;
* Список литературы отсутствует (Исправлено)&lt;br /&gt;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:19, 1 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:19, 1 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:03, 16 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* Рисунки в статье не подписаны и не пронумерованы. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:29, 21 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:29, 21 ноября 2016 (MSK) &lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:29, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18955</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18955"/>
		<updated>2016-11-22T13:13:00Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|700px|center|thumb|Рис.2. Информационный граф для алгоритма глобального стека. Ch - выбор отрезка интегрирования, Sq - вычисление площади на отрезке, Div - деление отрезка пополам, Tol - проверка достижения точности, Wr - запись отрезка в лок./глоб. стек, Sum - суммирование результатов]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene].&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.2. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.3. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18954</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18954"/>
		<updated>2016-11-22T13:06:17Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Graph1.png|500px|center|thumb|Информационный граф для алгоритма глобального стека]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene].&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.2. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.3. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Joinmek&amp;diff=18924</id>
		<title>Обсуждение участника:Joinmek</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Joinmek&amp;diff=18924"/>
		<updated>2016-11-22T09:36:20Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Статья [[Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки (2)]] ==&lt;br /&gt;
&lt;br /&gt;
* Нет краткой сводки свойств алгоритма в начале страницы (Исправлено)&lt;br /&gt;
* Нумерация подразделов не соответствует шаблону (Исправлено)&lt;br /&gt;
* Разделы 1.6 и 1.8 (6 и 8 в оригинальной статье) не заполнены (Исправлено)&lt;br /&gt;
* Картинка в разделе 1.7 (7) не является информационным графом (Исправлено)&lt;br /&gt;
** Если это информационный граф, из него следует, что алгоритм строго последовательный. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:29, 21 ноября 2016 (MSK)&lt;br /&gt;
** Приведённый граф очень мелкий, надписи практически нечитаемы. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:29, 21 ноября 2016 (MSK)&lt;br /&gt;
* Раздел 2.7 отсутствует (Исправлено)&lt;br /&gt;
* Список литературы отсутствует (Исправлено)&lt;br /&gt;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:19, 1 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:19, 1 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:03, 16 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* Рисунки в статье не подписаны и не пронумерованы. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:29, 21 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:29, 21 ноября 2016 (MSK) &lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:29, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18923</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18923"/>
		<updated>2016-11-22T09:35:17Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:1478004288745.png|500px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene].&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center|thumb|Рис.2. Зависимость времени работы алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center|thumb|Рис.3. Зависимость эффективности алгоритма от числа процессов и заданной точности]]&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18922</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18922"/>
		<updated>2016-11-22T09:33:09Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Общее описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center|thumb|Рис.1. Метод трапеций]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:1478004288745.png|500px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene].&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center]]&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18187</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18187"/>
		<updated>2016-11-18T15:37:46Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{assignment}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:1478004288745.png|500px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene].&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center]]&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Joinmek&amp;diff=18185</id>
		<title>Обсуждение участника:Joinmek</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Joinmek&amp;diff=18185"/>
		<updated>2016-11-18T15:37:02Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Статья Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки (2) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Статья [[Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки (2)]] ==&lt;br /&gt;
&lt;br /&gt;
* Нет краткой сводки свойств алгоритма в начале страницы (Исправлено)&lt;br /&gt;
* Нумерация подразделов не соответствует шаблону (Исправлено)&lt;br /&gt;
* Разделы 1.6 и 1.8 (6 и 8 в оригинальной статье) не заполнены (Исправлено)&lt;br /&gt;
* Картинка в разделе 1.7 (7) не является информационным графом (Исправлено)&lt;br /&gt;
* Раздел 2.7 отсутствует (Исправлено)&lt;br /&gt;
* Список литературы отсутствует (Исправлено)&lt;br /&gt;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:19, 1 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:19, 1 ноября 2016 (MSK) (Исправлено)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:03, 16 ноября 2016 (MSK) (Исправлено)&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18184</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18184"/>
		<updated>2016-11-18T15:33:08Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:1478004288745.png|500px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene].&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center]]&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18183</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18183"/>
		<updated>2016-11-18T15:32:46Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:1478004288745.png|500px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY] на языке C.&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а ,значит, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene].&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center]]&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18182</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18182"/>
		<updated>2016-11-18T15:32:03Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:1478004288745.png|500px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY].&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а ,значит, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков. Программа запускалась на [https://ru.wikipedia.org/wiki/Blue_Gene Blue Gene].&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center]]&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18181</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18181"/>
		<updated>2016-11-18T15:30:05Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования. Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&lt;br /&gt;
&lt;br /&gt;
[[Файл:1478004288745.png|500px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY].&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а ,значит, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center]]&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18179</id>
		<title>Участник:Joinmek/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Joinmek/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D0%BB%D0%B0_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B0%D0%B4%D0%B0%D0%BF%D1%82%D0%B8%D0%B2%D0%BD%D0%BE_%D1%81%D0%B3%D1%83%D1%89%D0%B0%D1%8E%D1%89%D0%B5%D0%B9%D1%81%D1%8F_%D1%81%D0%B5%D1%82%D0%BA%D0%B8&amp;diff=18179"/>
		<updated>2016-11-18T15:27:03Z</updated>

		<summary type="html">&lt;p&gt;Joinmek: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Joinmek|Можарова В. А.]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная идея большинства методов численного интегрирования состоит в замене подынтегральной функции на более простую, интеграл от которой легко вычисляется аналитически. При замене подынтегральной функции на полином нулевой, первой и второй степени получаются соответственно методы прямоугольников, трапеций и парабол (Симпсона).  &lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_trap.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках. Но чтобы посчитать интеграл с определенной точностью, мы не можем заранее узнать, какие размеры отрезков, нужно использовать, т. к. это зависит от самой функции. Для этого используют адаптивное разбиение участка интегрирования: размер конкретного отрезка определяется во время подсчета значения функции на нем самом. Если заданная точность не была достигнута на данном отрезке, он рекурсивно разбивается на несколько новых и функция подсчитывается уже на них. Таким образом, выигрыш от применения рассмотренного алгоритма рекурсивного разбиения  достигается за счет возможности использования сеток с разным числом узлов на разных участках интервала интегрирования.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть у нас есть интеграл, который надо вычислить с точностью ε:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_integral.png|200px|center]]&lt;br /&gt;
&lt;br /&gt;
Предположим, что в результате адаптации сетки для данной функции, на отрезке [A, B] получилась сетка, содержащая n + 1 узел. Тогда согласно методу трапеций, можно численно найти определенный интеграл от функции f(x) на отрезке [A, B]:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_common.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение J найденным с точностью ε, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_condition.png|300px|center]]&lt;br /&gt;
&lt;br /&gt;
где n&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; и  n&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; - количество узлов, образующих две разные сетки.&lt;br /&gt;
Пользуясь формулой&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_div.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
рекурсивно делим отрезки интегрирования до достижения на них заданной точности.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основное время работы алгоритма приходится на вычисление площадей на отрезках, получившихся в результате рекурсивного разбиения:&lt;br /&gt;
&lt;br /&gt;
[[Файл:vm_sq.png|400px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм итеративный. Сначала значение текущего отрезка равно общему отрезу интегрирования. На каждой итерации:&lt;br /&gt;
&lt;br /&gt;
1. На текущем отрезке считается приближенное значение интеграла&lt;br /&gt;
&lt;br /&gt;
2. Текущий отрезок делится пополам, и на получившихся отрезках высчитываются новые интегралы&lt;br /&gt;
&lt;br /&gt;
3. Если достигнута заданная точность, то&lt;br /&gt;
&lt;br /&gt;
3.1 Добавить значение интеграла текущего отрезка к общему интегралу&lt;br /&gt;
&lt;br /&gt;
3.2 Если стек пуст, то завершить алгоритм&lt;br /&gt;
&lt;br /&gt;
3.3 Достать из стека отрезок и присвоить его значение текущему отрезку.&lt;br /&gt;
&lt;br /&gt;
4. Иначе &lt;br /&gt;
&lt;br /&gt;
4.1 Один из получившихся маленьких отрезков положить в стек&lt;br /&gt;
&lt;br /&gt;
4.2 Текущему отрезку присвоить значение второго маленького отрезка&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для хранения отрезков интегрирования в данном алгоритме понадобится стек &amp;lt;ref&amp;gt;Якобовский М. В., Кулькова Е. Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. Москва, 2004.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
    sp=0  //    указатель вершины стека&lt;br /&gt;
    struct {&lt;br /&gt;
          A, B, fA, fB, s&lt;br /&gt;
    } stk[1000] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
У стека определен следующий набор операций:&lt;br /&gt;
&lt;br /&gt;
1.	PUT_INTO_STACK - положить элемент в стек&lt;br /&gt;
&lt;br /&gt;
    #define PUT_INTO_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        stk[sp].A=A&lt;br /&gt;
        stk[sp].B=B&lt;br /&gt;
        stk[sp].fA=fA&lt;br /&gt;
        stk[sp].fB=fB&lt;br /&gt;
        stk[sp].s=s&lt;br /&gt;
        sp++&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
2.	GET_FROM_STACK - взять верхний элемент из стека&lt;br /&gt;
    #define GET_FROM_STACK(A, B, fA, fB, s)&lt;br /&gt;
    {&lt;br /&gt;
        sp--&lt;br /&gt;
        A=stk[sp].A&lt;br /&gt;
        B=stk[sp].B&lt;br /&gt;
        fA=stk[sp].fA&lt;br /&gt;
        fB=stk[sp].fB&lt;br /&gt;
        s=stk[sp].s&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
3.	STACK_IS_NOT_FREE - проверить стек на наличие в нем элементов&lt;br /&gt;
    #define STACK_IS_NOT_FREE (sp &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    LocalStack (A, B)&lt;br /&gt;
    {&lt;br /&gt;
        // В переменной J храним ответ&lt;br /&gt;
        J=0&lt;br /&gt;
        // Вычисляем значение функции на границах интегрируемого отрезка&lt;br /&gt;
        fA = f(A)&lt;br /&gt;
        fB = f(B)&lt;br /&gt;
        // Вычисляем площадь с помощью метода трапеций&lt;br /&gt;
        sAB = (fA + fB) * (B - A) / 2&lt;br /&gt;
        while (1)&lt;br /&gt;
        {&lt;br /&gt;
            C = (A + B) / 2  // Находим середину отрезка&lt;br /&gt;
            fc = f(C)  // Вычисляем в середине значение функции&lt;br /&gt;
            // Считаем приближенное значение интеграла на получившихся отрезках&lt;br /&gt;
            sAC = (fA + fC) * (C - A) / 2&lt;br /&gt;
            sCB = (fC + fB ) * (B-C) / 2&lt;br /&gt;
            sACB = sAC + sCB&lt;br /&gt;
            if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&lt;br /&gt;
                PUT_INTO_STACK(A, C, fA, fC, sAC)  // то один из отрезков кладем в стек&lt;br /&gt;
                A = C  // и решаем задачу для оставшегося отрезка&lt;br /&gt;
                fA = fC&lt;br /&gt;
                sAB = sCB&lt;br /&gt;
            } else  // Если заданная точность достигнута&lt;br /&gt;
            {&lt;br /&gt;
                J += sACB  // то к общей сумме добавляем полученное число&lt;br /&gt;
                if (STACK_IS_NOT_FREE)  // если стек пуст, то цикл завершается - ответ получен&lt;br /&gt;
                    break &lt;br /&gt;
                GET_FROM_STACK(A, B, fA, fB, sAB)  // иначе берем из стека новый отрезок&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        return J &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Количество делений отрезков интегрирования зависит от сложности входной функции, поэтому нельзя оценить сложность данного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
В параллельной версии алгоритма, появляется дополнительный глобальный стек, доступ к которому имеют все процессы. Он находится в общей памяти и так же содержит границы отрезков. Процессы обмениваются с ним данными, распределяя таким образом отрезки между собой. Когда в локальном стеке заканчиваются элементы, процесс обращается к глобальному стеку, чтобы получить их. Причем любой процесс может отдавать ему отрезки, если глобальный стек пуст в то время, как локальный имеет более одного отрезка. Изначально в глобальный стек помещается один отрезок, соответствующий участку интегрирования.&lt;br /&gt;
&lt;br /&gt;
[[Файл:1478004288745.png|500px|center]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Сложность параллельного алгоритма нельзя оценить из-за зависимости от сложности интегрируемой функции так же, как и у последовательного алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
На вход алгоритму подается подынтегральная функция, пределы интегрирования и значение точности, с которой нужно посчитать заданный интеграл.&lt;br /&gt;
На выходе: число – значение определенного интеграла.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
1.	Алгоритм адаптируется под сложность функции&lt;br /&gt;
&lt;br /&gt;
2.	Для каждого узла сетки значение функции высчитывается один раз&lt;br /&gt;
&lt;br /&gt;
3.	В параллельной версии алгоритма не требуется управляющий процесс&lt;br /&gt;
&lt;br /&gt;
4.	Алгоритм является детерминированным&lt;br /&gt;
&lt;br /&gt;
5.	Степень исхода вершины информационного графа равна 1, т. к. результат работы каждого процесса в дальнейшем используется только 1 раз.&lt;br /&gt;
&lt;br /&gt;
6.	В представленном параллельном алгоритме сбалансирована загрузка процессов, т. к. при завершении  своих задач каждый процессор обращается за новыми в глобальный стек&lt;br /&gt;
&lt;br /&gt;
7.	Данный алгоритм является устойчивым&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений === &lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Автором данной статьи была выбрана реализация [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH AACHEN UNIVERSITY].&lt;br /&gt;
Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а ,значит, ограничена одним узлом.&lt;br /&gt;
&lt;br /&gt;
Тогда было принято решение модифицировать данный алгоритм, разделив отрезок интегрирования на равные части, количество которых равно количеству запущенных процессов ([https://bitbucket.org/FilmSearchers/globalstack/src Code]). Каждый из них считает интеграл на своем участке, а затем отправляет результат нулевому процессу, на котором происходит суммирование.&lt;br /&gt;
&lt;br /&gt;
Чтобы протестировать систему была выбрана функция sin(x). Вычисление интеграла запускается с разными значениями точности, чтобы пронаблюдать за изменением времени в зависимости от количества итоговых отрезков.&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Time1.png|800px|center]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:VM_Efficiency1.png|800px|center]]&lt;br /&gt;
&lt;br /&gt;
Система запускалась на количестве процессов от 8 до 128, начиная с 16 процессов шаг был равен 16. Точность градировалась от 0.00001 до 0.0001 с величиной шага 0.00001. Максимальная эффективность была достигнута на 8 процессах и равна 0.9389. Минимальная - на 128, ее величина 0.2914.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В России данный алгоритм реализован Якобовским М. В.&lt;br /&gt;
&lt;br /&gt;
За границей осуществила реализацию компания  [https://doc.itc.rwth-aachen.de/display/CCP/Parallelizing+an+Algorithm+for+Adaptive+Integration+with+OpenMP RWTH Compute Cluster];  группа исследователей во главе с [http://on-demand.gputechconf.com/gtc/2013/poster/pdf/P0237_KameshArumugam.pdf Kamesh Arumugam];  а также команда Berntsen, J.; Espelid, T. O.; and Genz, A.&amp;lt;ref&amp;gt;Berntsen, J.; Espelid, T. O.; and Genz, A. “An Adaptive Algorithm for the Approximate Calculation of Multiple Integrals.” ACM Transactions on Mathematical Software Vol. 17, No. 4, December 1991, Pages 437-451.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Joinmek</name></author>
	</entry>
</feed>