<?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=Stalles</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=Stalles"/>
	<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/Stalles"/>
	<updated>2026-10-22T20:24: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%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:Stalles&amp;diff=20963</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20963"/>
		<updated>2016-12-14T20:48:04Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.5 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных в работе алгоритмах как раз и используется метод трапеций. Отличие только в том, что в оригинальном методе сетка равномерная, а в приведённых в работе - нет;кроме этого момента, все формулы и условия остаются теми же.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:40, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&lt;br /&gt;
&lt;br /&gt;
Есть ли в реализации глобальный стек, который описан в работе?  &lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=20961</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=20961"/>
		<updated>2016-12-14T20:41:38Z</updated>

		<summary type="html">&lt;p&gt;Stalles: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{Assignment}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;+ 1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту. Имеется в виду тот факт, что при рекурсивном вызове процесс, вызвавший IntTrap для двух подотрезков, будет ожидать завершения работы вызванных функций. При этом выполнять какую-то другую работу он не будет. При достаточно большой глубине рекурсии возникнет ситуация, когда много процессоров будут ожидать выполнения работы рекурсивных вызовов, сами при этом простаивая. При ограниченном числе процессоров это сильно замедлит выполнение программы, приблизив его к времени работы последовательного варианта. Однако, если учесть, что время порождения параллельных процессов существенно не равно нулю, время работы такой программы может стать даже больше времени выполнения последовательной версии.&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;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, если это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. В противном случае объем представления функции, подаваемый на вход, нельзя оценить. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI. При запусках использовалась связка openmpi/1.8.4-gcc + gcc/5.2.0, реализация - тут -&amp;gt; [https://github.com/Stalles/superprak1 ссылка на реализацию.]&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. В качестве тестируемой функции была выбрана функция &amp;lt;math&amp;gt;1/(x^2) * (sin(1/x))^2&amp;lt;/math&amp;gt;. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
Проведем оценки масштабируемости:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По числу процессов&#039;&#039;&#039; — при увеличении числа процессов эффективность уменьшается на всей области рассматриваемых значений, причем темп убывания замедляется с ростом числа процессов. Кроме того, видно, что при более,чем 40 процессов эффективность становится крайне низкой.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По размеру задачи (точности вычислений)&#039;&#039;&#039; — при увеличении точности вычислений и при постоянном числе процессов эффективность вычислений остаётся практически неизменяемой, очень медленно снижается. Это объясняется тем, что некоторые отрезки интегрирования имеют большую трудоёмкость из-за негладкости функции. Процессы, которым достались такие отрезки, и определяют время работы всей программы.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20958</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20958"/>
		<updated>2016-12-14T20:33:25Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.5 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных в работе алгоритмах как раз и используется метод трапеций. Отличие только в том, что в оригинальном методе сетка равномерная, а в приведённых в работе - нет, все формулы и условия остаются теми же.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:40, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&lt;br /&gt;
&lt;br /&gt;
Есть ли в реализации глобальный стек, который описан в работе?  &lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20956</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20956"/>
		<updated>2016-12-14T20:30:50Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.2.3 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных в работе алгоритмах как раз и используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:40, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&lt;br /&gt;
&lt;br /&gt;
Есть ли в реализации глобальный стек, который описан в работе?  &lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=20954</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=20954"/>
		<updated>2016-12-14T20:30:37Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Метод локального стека */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;+ 1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту. Имеется в виду тот факт, что при рекурсивном вызове процесс, вызвавший IntTrap для двух подотрезков, будет ожидать завершения работы вызванных функций. При этом выполнять какую-то другую работу он не будет. При достаточно большой глубине рекурсии возникнет ситуация, когда много процессоров будут ожидать выполнения работы рекурсивных вызовов, сами при этом простаивая. При ограниченном числе процессоров это сильно замедлит выполнение программы, приблизив его к времени работы последовательного варианта. Однако, если учесть, что время порождения параллельных процессов существенно не равно нулю, время работы такой программы может стать даже больше времени выполнения последовательной версии.&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;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, если это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. В противном случае объем представления функции, подаваемый на вход, нельзя оценить. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI. При запусках использовалась связка openmpi/1.8.4-gcc + gcc/5.2.0, реализация - тут -&amp;gt; [https://github.com/Stalles/superprak1 ссылка на реализацию.]&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. В качестве тестируемой функции была выбрана функция &amp;lt;math&amp;gt;1/(x^2) * (sin(1/x))^2&amp;lt;/math&amp;gt;. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
Проведем оценки масштабируемости:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По числу процессов&#039;&#039;&#039; — при увеличении числа процессов эффективность уменьшается на всей области рассматриваемых значений, причем темп убывания замедляется с ростом числа процессов. Кроме того, видно, что при более,чем 40 процессов эффективность становится крайне низкой.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По размеру задачи (точности вычислений)&#039;&#039;&#039; — при увеличении точности вычислений и при постоянном числе процессов эффективность вычислений остаётся практически неизменяемой, очень медленно снижается. Это объясняется тем, что некоторые отрезки интегрирования имеют большую трудоёмкость из-за негладкости функции. Процессы, которым достались такие отрезки, и определяют время работы всей программы.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20951</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20951"/>
		<updated>2016-12-14T20:28:04Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 2.4 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных в работе алгоритмах как раз и используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:40, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&lt;br /&gt;
&lt;br /&gt;
Есть ли в реализации глобальный стек, который описан в работе?  &lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20950</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20950"/>
		<updated>2016-12-14T20:27:42Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.9 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных в работе алгоритмах как раз и используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:40, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&lt;br /&gt;
&lt;br /&gt;
Есть ли в реализации глобальный стек, который описан в работе?  &lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20949</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20949"/>
		<updated>2016-12-14T20:27:31Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.2.3 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных в работе алгоритмах как раз и используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:40, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&lt;br /&gt;
&lt;br /&gt;
Есть ли в реализации глобальный стек, который описан в работе?  &lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20948</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20948"/>
		<updated>2016-12-14T20:27:21Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.2.2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Исправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных в работе алгоритмах как раз и используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:40, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&lt;br /&gt;
&lt;br /&gt;
Есть ли в реализации глобальный стек, который описан в работе?  &lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=20947</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=20947"/>
		<updated>2016-12-14T20:26:43Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Математическое описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;+ 1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту. Имеется в виду тот факт, что при рекурсивном вызове процесс, вызвавший IntTrap для двух подотрезков, будет ожидать завершения работы вызванных функций. При этом выполнять какую-то другую работу он не будет. При достаточно большой глубине рекурсии возникнет ситуация, когда много процессоров будут ожидать выполнения работы рекурсивных вызовов, сами при этом простаивая. При ограниченном числе процессоров это сильно замедлит выполнение программы, приблизив его к времени работы последовательного варианта. Однако, если учесть, что время порождения параллельных процессов существенно не равно нулю, время работы такой программы может стать даже больше времени выполнения последовательной версии.&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;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, если это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. В противном случае объем представления функции, подаваемый на вход, нельзя оценить. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI. При запусках использовалась связка openmpi/1.8.4-gcc + gcc/5.2.0, реализация - тут -&amp;gt; [https://github.com/Stalles/superprak1 ссылка на реализацию.]&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. В качестве тестируемой функции была выбрана функция &amp;lt;math&amp;gt;1/(x^2) * (sin(1/x))^2&amp;lt;/math&amp;gt;. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
Проведем оценки масштабируемости:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По числу процессов&#039;&#039;&#039; — при увеличении числа процессов эффективность уменьшается на всей области рассматриваемых значений, причем темп убывания замедляется с ростом числа процессов. Кроме того, видно, что при более,чем 40 процессов эффективность становится крайне низкой.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По размеру задачи (точности вычислений)&#039;&#039;&#039; — при увеличении точности вычислений и при постоянном числе процессов эффективность вычислений остаётся практически неизменяемой, очень медленно снижается. Это объясняется тем, что некоторые отрезки интегрирования имеют большую трудоёмкость из-за негладкости функции. Процессы, которым достались такие отрезки, и определяют время работы всей программы.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20941</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20941"/>
		<updated>2016-12-14T20:07:59Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.5 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных в работе алгоритмах как раз и используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:40, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&lt;br /&gt;
&lt;br /&gt;
Есть ли в реализации глобальный стек, который описан в работе?  &lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20940</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20940"/>
		<updated>2016-12-14T20:07:10Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.2.3 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных алгоритмах используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:40, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&lt;br /&gt;
&lt;br /&gt;
Есть ли в реализации глобальный стек, который описан в работе?  &lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20939</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20939"/>
		<updated>2016-12-14T20:00:20Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 2.4 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных алгоритмах используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:40, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&lt;br /&gt;
&lt;br /&gt;
Есть ли в реализации глобальный стек, который описан в работе?  &lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20938</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20938"/>
		<updated>2016-12-14T19:54:54Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 2.4 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных алгоритмах используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:40, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&lt;br /&gt;
&lt;br /&gt;
Есть ли в реализации глобальный стек, который описан в работе?  &lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
-Правильно. &lt;br /&gt;
&lt;br /&gt;
-&#039;&#039;&#039;&amp;quot;...относятся к несколько другому алгоритму...&amp;quot;&#039;&#039;&#039; - к другому относительно чего?&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.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20937</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20937"/>
		<updated>2016-12-14T19:47:24Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 2.4 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных алгоритмах используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:40, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&lt;br /&gt;
&lt;br /&gt;
Есть ли в реализации глобальный стек, который описан в работе?  &lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
-Правильно. &lt;br /&gt;
&lt;br /&gt;
-&#039;&#039;&#039;&amp;quot;...относятся к несколько другому алгоритму...&amp;quot;&#039;&#039;&#039; - к другому относительно чего?&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.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20936</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20936"/>
		<updated>2016-12-14T19:43:46Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 2.4 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных алгоритмах используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:40, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&lt;br /&gt;
&lt;br /&gt;
Есть ли в реализации глобальный стек, который описан в работе?  &lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;quot;относятся к несколько другому алгоритму&amp;quot;&#039;&#039;&#039; - к другому относительно чего?&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Статья [[Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки]] ==&lt;br /&gt;
&lt;br /&gt;
=== Замечания по тексту ===&lt;br /&gt;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20935</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20935"/>
		<updated>2016-12-14T19:43:28Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 2.4 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных алгоритмах используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:40, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&lt;br /&gt;
&lt;br /&gt;
Есть ли в реализации глобальный стек, который описан в работе?  &lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;относятся к несколько другому алгоритму&#039;&#039;&#039; - к другому относительно чего?&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Статья [[Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки]] ==&lt;br /&gt;
&lt;br /&gt;
=== Замечания по тексту ===&lt;br /&gt;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20934</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20934"/>
		<updated>2016-12-14T19:40:37Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.9 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных алгоритмах используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:40, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=20933</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=20933"/>
		<updated>2016-12-14T19:40:09Z</updated>

		<summary type="html">&lt;p&gt;Stalles: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;+ 1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, если это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. В противном случае объем представления функции, подаваемый на вход, нельзя оценить. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI. При запусках использовалась связка openmpi/1.8.4-gcc + gcc/5.2.0, реализация - тут -&amp;gt; [https://github.com/Stalles/superprak1 ссылка на реализацию.]&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. В качестве тестируемой функции была выбрана функция &amp;lt;math&amp;gt;1/(x^2) * (sin(1/x))^2&amp;lt;/math&amp;gt;. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
Проведем оценки масштабируемости:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По числу процессов&#039;&#039;&#039; — при увеличении числа процессов эффективность уменьшается на всей области рассматриваемых значений, причем темп убывания замедляется с ростом числа процессов. Кроме того, видно, что при более,чем 40 процессов эффективность становится крайне низкой.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По размеру задачи (точности вычислений)&#039;&#039;&#039; — при увеличении точности вычислений и при постоянном числе процессов эффективность вычислений остаётся практически неизменяемой, очень медленно снижается. Это объясняется тем, что некоторые отрезки интегрирования имеют большую трудоёмкость из-за негладкости функции. Процессы, которым достались такие отрезки, и определяют время работы всей программы.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=20932</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=20932"/>
		<updated>2016-12-14T19:39:18Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Входные и выходные данные алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность))&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, если это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. В противном случае объем представления функции, подаваемый на вход, нельзя оценить. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI. При запусках использовалась связка openmpi/1.8.4-gcc + gcc/5.2.0, реализация - тут -&amp;gt; [https://github.com/Stalles/superprak1 ссылка на реализацию.]&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. В качестве тестируемой функции была выбрана функция &amp;lt;math&amp;gt;1/(x^2) * (sin(1/x))^2&amp;lt;/math&amp;gt;. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
Проведем оценки масштабируемости:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По числу процессов&#039;&#039;&#039; — при увеличении числа процессов эффективность уменьшается на всей области рассматриваемых значений, причем темп убывания замедляется с ростом числа процессов. Кроме того, видно, что при более,чем 40 процессов эффективность становится крайне низкой.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По размеру задачи (точности вычислений)&#039;&#039;&#039; — при увеличении точности вычислений и при постоянном числе процессов эффективность вычислений остаётся практически неизменяемой, очень медленно снижается. Это объясняется тем, что некоторые отрезки интегрирования имеют большую трудоёмкость из-за негладкости функции. Процессы, которым достались такие отрезки, и определяют время работы всей программы.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20931</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20931"/>
		<updated>2016-12-14T19:37:46Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.5 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:37, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных алгоритмах используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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=20930</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=20930"/>
		<updated>2016-12-14T19:36:55Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.5 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK) К работе есть несколько замечаний по содержанию (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.1 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Наверное, на отрезках рассчитываются интегралы от новых функций, а их значения уже суммируются?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2?&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.6 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
В качестве примера нужно привести хотя бы оценки, определяя разбиение отрезка (глубину, неравномерность и пр.) без учета функции.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.7 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Хорошо бы пояснить в чем достоинства и недостатки такого подхода, по сравнению с тем, что описывается ранее?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.8 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
См. пункт 1.6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.10 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3.	В параллельной версии алгоритма не требуется управляющий процесс&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно здесь уточнить, что рассматривается алгоритм с общей памятью. Будет ли нужен управляющий процесс при реализации глобального стека на системах с распределенной памятью?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4.	Алгоритм является детерминированным&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Влияют ли на свойство детерминированности алгоритма выполнение свойства ассоциативности для операции сложения, а также свойства входной функции?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;7.	Данный алгоритм является устойчивым&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчив для любой функции?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, на архитектуре Blue Gene, на которой она тестировалась, ограничена одним узлом.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
То, что данная реализация предназначена для систем с общей памятью, не означает, что она не является масштабируемой.&lt;br /&gt;
Почему нет графиков масштабируемости по ядрам одного узла для алгоритма с адаптивной сеткой, где глобальный стек реализован для общей памяти?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&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>Stalles</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=20929</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=20929"/>
		<updated>2016-12-14T19:36:40Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.5 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK) К работе есть несколько замечаний по содержанию (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.1 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Наверное, на отрезках рассчитываются интегралы от новых функций, а их значения уже суммируются?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2?&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:36, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных алгоритмах используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.6 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
В качестве примера нужно привести хотя бы оценки, определяя разбиение отрезка (глубину, неравномерность и пр.) без учета функции.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.7 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Хорошо бы пояснить в чем достоинства и недостатки такого подхода, по сравнению с тем, что описывается ранее?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.8 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
См. пункт 1.6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.10 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3.	В параллельной версии алгоритма не требуется управляющий процесс&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно здесь уточнить, что рассматривается алгоритм с общей памятью. Будет ли нужен управляющий процесс при реализации глобального стека на системах с распределенной памятью?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4.	Алгоритм является детерминированным&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Влияют ли на свойство детерминированности алгоритма выполнение свойства ассоциативности для операции сложения, а также свойства входной функции?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;7.	Данный алгоритм является устойчивым&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчив для любой функции?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, на архитектуре Blue Gene, на которой она тестировалась, ограничена одним узлом.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
То, что данная реализация предназначена для систем с общей памятью, не означает, что она не является масштабируемой.&lt;br /&gt;
Почему нет графиков масштабируемости по ядрам одного узла для алгоритма с адаптивной сеткой, где глобальный стек реализован для общей памяти?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&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>Stalles</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=20928</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=20928"/>
		<updated>2016-12-14T19:36:27Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.5 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK) К работе есть несколько замечаний по содержанию (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.1 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Чтобы посчитать определенный интеграл, участок интегрирования делится на отрезки, на которых подсчитывается значение новой (упрощенной) функции, после чего значения суммируются на всех отрезках.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Наверное, на отрезках рассчитываются интегралы от новых функций, а их значения уже суммируются?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if (|sAB - sACB| ≥ ε|sACB|) {  // Если заданная точность не достигнута&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2?&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:34, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Не отличается, так как во всех перечисленных алгоритмах используется метод трапеций.&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.6 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
В качестве примера нужно привести хотя бы оценки, определяя разбиение отрезка (глубину, неравномерность и пр.) без учета функции.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.7 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Существуют реализации, где локальные стеки полностью отсутствуют, а все отрезки хранятся в глобальном стеке.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Хорошо бы пояснить в чем достоинства и недостатки такого подхода, по сравнению с тем, что описывается ранее?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.8 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
См. пункт 1.6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.10 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3.	В параллельной версии алгоритма не требуется управляющий процесс&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно здесь уточнить, что рассматривается алгоритм с общей памятью. Будет ли нужен управляющий процесс при реализации глобального стека на системах с распределенной памятью?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4.	Алгоритм является детерминированным&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Влияют ли на свойство детерминированности алгоритма выполнение свойства ассоциативности для операции сложения, а также свойства входной функции?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;7.	Данный алгоритм является устойчивым&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчив для любой функции?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 04:46, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данная реализация не является масштабируемой, т. к. она основана на механизме общей памяти, а, значит, на архитектуре Blue Gene, на которой она тестировалась, ограничена одним узлом.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
То, что данная реализация предназначена для систем с общей памятью, не означает, что она не является масштабируемой.&lt;br /&gt;
Почему нет графиков масштабируемости по ядрам одного узла для алгоритма с адаптивной сеткой, где глобальный стек реализован для общей памяти?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&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>Stalles</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:Stalles&amp;diff=20927</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20927"/>
		<updated>2016-12-14T19:33:51Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.5 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20926</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20926"/>
		<updated>2016-12-14T19:32:56Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.2.3 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]] 22:32, 14 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Поправлено&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20925</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20925"/>
		<updated>2016-12-14T19:32:14Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.2.3 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&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.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:Stalles&amp;diff=20924</id>
		<title>Обсуждение участника:Stalles</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:Stalles&amp;diff=20924"/>
		<updated>2016-12-14T19:31:55Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Пункт 1.2.3 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK) Хорошее описание метода и алгоритма. Есть несколько замечаний по содержанию и вопросы по исследованию масштабируемости (ниже с моей подписью)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.2 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;gt;, и недоступны программисту.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Что подразумевается под недоступностью стека для программиста, если далее рассматривается работа с этим стеком? Нужно пояснить это утверждение.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.2.3 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Данный алгоритм использует массив данных, доступ к которым осуществляется по принципу стека - первым удаляется последний из добавленных элементов. Теперь у разных процессов есть возможность обрабатывать отрезки независимо друг от друга.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Нужно описать немного подробнее, как из определения стека появляется возможность обрабатывать отрезки независимо друг от друга.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм локального стека можно считать наилучшим из перечисленных последовательных методов для разработки его параллельной версии.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Где в работе были перечислены последовательные методы, которые здесь сравниваются? С какой точки зрения локальный стек является наилучшим, по сравнению с ними?&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Бобцов_Борис|Бобцов Борис]]&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.5 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Не отличается ли здесь условие на деление отрезка от того, что приведено в пункте 1.2.1?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 1.9 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; - это уже не любой вид представления функции.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Пункт 2.4 ==&lt;br /&gt;
--[[Участник:Evgeny Mortikov|Evgeny Mortikov]] ([[Обсуждение участника:Evgeny Mortikov|обсуждение]]) 05:06, 3 декабря 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
Правильно ли я понимаю, что представленные результаты по исследованию масштабируемости относятся к несколько другому алгоритму – рассматривается равномерное разбиение отрезка по MPI процессам, а в рамках каждого рассчитывается алгоритм с адаптивной сеткой? Можно ли по полученным результатам судить о масштабируемости исследуемого алгоритма? Нужно это пояснить в работе.&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;
* В разделе 1.4 требуется привести описание алгоритма на верхнем уровне (на основе макроопераций), а не то, что там сейчас. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.5 кроме кода нужны также текстовые пояснения. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
* Граф, приведённый в разделе 1.7 не является информационным графом (ациклический граф, вершины - операции, дуги - информационные зависимости). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 18:25, 1 ноября 2016 (MSK)&lt;br /&gt;
** Граф на рис.1 не является графом алгоритма, как написано в подписи к нему. Граф на рис.2 также является каким-то гибридом информационного графа и графа управления - в частности, содержит ветвления, которых в информационном графе быть не должно. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:57, 14 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 1.9 должны быть оценки объёмов входных и выходных данных. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* В части 2 должны быть все разделы 2.1-2.7 (некоторые могут быть пустыми). [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:00, 14 ноября 2016 (MSK)&lt;br /&gt;
* Не заполнен раздел 2.4 описания. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 16:04, 16 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведены все параметры запуска теста - какой компилятор, с какими опциями использовался, какие версии библиотек, на каких узлах проводился запуск и т.д. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* В разделе 2.4 не приведена использованная в экспериментах реализация алгоритма. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK) &lt;br /&gt;
** Замечание не исправлено. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 15:21, 28 ноября 2016 (MSK)&lt;br /&gt;
* Из графиков в разделе 2.4 следует сделать выводы о масштабируемости реализации. [[Участник:ASA|Александр Сергеевич Антонов]] ([[Обсуждение участника:ASA|обсуждение]]) 14:31, 21 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=20923</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=20923"/>
		<updated>2016-12-14T19:30:43Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Метод локального стека */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность))&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI. При запусках использовалась связка openmpi/1.8.4-gcc + gcc/5.2.0, реализация - тут -&amp;gt; [https://github.com/Stalles/superprak1 ссылка на реализацию.]&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. В качестве тестируемой функции была выбрана функция &amp;lt;math&amp;gt;1/(x^2) * (sin(1/x))^2&amp;lt;/math&amp;gt;. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
Проведем оценки масштабируемости:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По числу процессов&#039;&#039;&#039; — при увеличении числа процессов эффективность уменьшается на всей области рассматриваемых значений, причем темп убывания замедляется с ростом числа процессов. Кроме того, видно, что при более,чем 40 процессов эффективность становится крайне низкой.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По размеру задачи (точности вычислений)&#039;&#039;&#039; — при увеличении точности вычислений и при постоянном числе процессов эффективность вычислений остаётся практически неизменяемой, очень медленно снижается. Это объясняется тем, что некоторые отрезки интегрирования имеют большую трудоёмкость из-за негладкости функции. Процессы, которым достались такие отрезки, и определяют время работы всей программы.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19765</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19765"/>
		<updated>2016-11-30T20:56:35Z</updated>

		<summary type="html">&lt;p&gt;Stalles: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность))&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI. При запусках использовалась связка openmpi/1.8.4-gcc + gcc/5.2.0, реализация - тут -&amp;gt; [https://github.com/Stalles/superprak1 ссылка на реализацию.]&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. В качестве тестируемой функции была выбрана функция &amp;lt;math&amp;gt;1/(x^2) * (sin(1/x))^2&amp;lt;/math&amp;gt;. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
Проведем оценки масштабируемости:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По числу процессов&#039;&#039;&#039; — при увеличении числа процессов эффективность уменьшается на всей области рассматриваемых значений, причем темп убывания замедляется с ростом числа процессов. Кроме того, видно, что при более,чем 40 процессов эффективность становится крайне низкой.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По размеру задачи (точности вычислений)&#039;&#039;&#039; — при увеличении точности вычислений и при постоянном числе процессов эффективность вычислений остаётся практически неизменяемой, очень медленно снижается. Это объясняется тем, что некоторые отрезки интегрирования имеют большую трудоёмкость из-за негладкости функции. Процессы, которым достались такие отрезки, и определяют время работы всей программы.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19764</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19764"/>
		<updated>2016-11-30T20:54:37Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность))&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI. При запусках использовалась связка openmpi/1.8.4-gcc + gcc/5.2.0, реализация - тут -&amp;gt; [https://github.com/Stalles/superprak1 ссылка на реализацию.]&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. В качестве тестируемой функции была выбрана функция &amp;lt;math&amp;gt;1/(x^2) * (sin(1/x))^2&amp;lt;/math&amp;gt;. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
Проведем оценки масштабируемости:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По числу процессов&#039;&#039;&#039; — при увеличении числа процессов эффективность уменьшается на всей области рассматриваемых значений, причем темп убывания замедляется с ростом числа процессов. Кроме того, видно, что при более,чем 40 процессов эффективность становится крайне низкой.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По размеру задачи (точности вычислений)&#039;&#039;&#039; — при увеличении точности вычислений и при постоянном числе процессов эффективность вычислений остаётся практически неизменяемой, очень медленно снижается. Это объясняется тем, что некоторые отрезки интегрирования имеют большую трудоёмкость из-за негладкости функции. Процессы, которым достались такие отрезки, и определяют время работы всей программы.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19761</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19761"/>
		<updated>2016-11-30T20:41:12Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность))&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI. [https://github.com/Stalles/superprak1 ссылка на реализацию]&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. В качестве тестируемой функции была выбрана функция &amp;lt;math&amp;gt;1/(x^2) * (sin(1/x))^2&amp;lt;/math&amp;gt;. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
Проведем оценки масштабируемости:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По числу процессов&#039;&#039;&#039; — при увеличении числа процессов эффективность уменьшается на всей области рассматриваемых значений, причем темп убывания замедляется с ростом числа процессов. Кроме того, видно, что при более,чем 40 процессов эффективность становится крайне низкой.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По размеру задачи (точности вычислений)&#039;&#039;&#039; — при увеличении точности вычислений и при постоянном числе процессов эффективность вычислений остаётся практически неизменяемой, очень медленно снижается. Это объясняется тем, что некоторые отрезки интегрирования имеют большую трудоёмкость из-за негладкости функции. Процессы, которым достались такие отрезки, и определяют время работы всей программы.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19293</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19293"/>
		<updated>2016-11-27T18:51:18Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность))&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. В качестве тестируемой функции была выбрана функция &amp;lt;math&amp;gt;1/(x^2) * (sin(1/x))^2&amp;lt;/math&amp;gt;. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
Проведем оценки масштабируемости:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По числу процессов&#039;&#039;&#039; — при увеличении числа процессов эффективность уменьшается на всей области рассматриваемых значений, причем темп убывания замедляется с ростом числа процессов. Кроме того, видно, что при более,чем 40 процессов эффективность становится крайне низкой.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По размеру задачи (точности вычислений)&#039;&#039;&#039; — при увеличении точности вычислений и при постоянном числе процессов эффективность вычислений остаётся практически неизменяемой, очень медленно снижается. Это объясняется тем, что некоторые отрезки интегрирования имеют большую трудоёмкость из-за негладкости функции. Процессы, которым достались такие отрезки, и определяют время работы всей программы.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19292</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19292"/>
		<updated>2016-11-27T18:51:05Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность))&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. В качестве тестируемой функции была выбрана предложенная в   функция &amp;lt;math&amp;gt;1/(x^2) * (sin(1/x))^2&amp;lt;/math&amp;gt;. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
Проведем оценки масштабируемости:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По числу процессов&#039;&#039;&#039; — при увеличении числа процессов эффективность уменьшается на всей области рассматриваемых значений, причем темп убывания замедляется с ростом числа процессов. Кроме того, видно, что при более,чем 40 процессов эффективность становится крайне низкой.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По размеру задачи (точности вычислений)&#039;&#039;&#039; — при увеличении точности вычислений и при постоянном числе процессов эффективность вычислений остаётся практически неизменяемой, очень медленно снижается. Это объясняется тем, что некоторые отрезки интегрирования имеют большую трудоёмкость из-за негладкости функции. Процессы, которым достались такие отрезки, и определяют время работы всей программы.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19291</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19291"/>
		<updated>2016-11-27T18:26:42Z</updated>

		<summary type="html">&lt;p&gt;Stalles: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность))&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. В качестве тестируемой функции была выбрана предложенная в   функция Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
Проведем оценки масштабируемости:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По числу процессов&#039;&#039;&#039; — при увеличении числа процессов эффективность уменьшается на всей области рассматриваемых значений, причем темп убывания замедляется с ростом числа процессов. Кроме того, видно, что при более,чем 40 процессов эффективность становится крайне низкой.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По размеру задачи (точности вычислений)&#039;&#039;&#039; — при увеличении точности вычислений и при постоянном числе процессов эффективность вычислений остаётся практически неизменяемой, очень медленно снижается. Это объясняется тем, что некоторые отрезки интегрирования имеют большую трудоёмкость из-за негладкости функции. Процессы, которым достались такие отрезки, и определяют время работы всей программы.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19290</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19290"/>
		<updated>2016-11-27T18:26:30Z</updated>

		<summary type="html">&lt;p&gt;Stalles: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность))&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. В качестве тестируемой функции была выбрана предложенная в   функция Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
Проведем оценки масштабируемости:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По числу процессов&#039;&#039;&#039; — при увеличении числа процессов эффективность уменьшается на всей области рассматриваемых значений, причем темп убывания замедляется с ростом числа процессов. Кроме того, видно, что при более,чем 40 процессов эффективность становится крайне низкой.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По размеру задачи (точности вычислений)&#039;&#039;&#039; — при увеличении точности вычислений и при постоянном числе процессов эффективность вычислений остаётся практически неизменяемой, очень медленно снижается. Это объясняется тем, что некоторые отрезки интегрирования имеют большую трудоёмкость из-за негладкости функции. Процессы, которым достались такие отрезки, и определяют время работы всей программы.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19289</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19289"/>
		<updated>2016-11-27T18:24:28Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt; log(n) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность))&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. В качестве тестируемой функции была выбрана предложенная в   функция Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
Проведем оценки масштабируемости:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По числу процессов&#039;&#039;&#039; — при увеличении числа процессов эффективность уменьшается на всей области рассматриваемых значений, причем темп убывания замедляется с ростом числа процессов. Кроме того, видно, что при более,чем 40 процессов эффективность становится крайне низкой.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По размеру задачи (точности вычислений)&#039;&#039;&#039; — при увеличении точности вычислений и при постоянном числе процессов эффективность вычислений остаётся практически неизменяемой, очень медленно снижается. Это объясняется тем, что некоторые отрезки интегрирования имеют большую трудоёмкость из-за негладкости функции. Процессы, которым достались такие отрезки, и определяют время работы всей программы.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19288</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19288"/>
		<updated>2016-11-27T18:20:22Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt; log(n) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность))&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
Проведем оценки масштабируемости:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По числу процессов&#039;&#039;&#039; — при увеличении числа процессов эффективность уменьшается на всей области рассматриваемых значений, причем темп убывания замедляется с ростом числа процессов. Кроме того, видно, что при более,чем 40 процессов эффективность становится крайне низкой.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;По размеру задачи (точности вычислений)&#039;&#039;&#039; — при увеличении точности вычислений и при постоянном числе процессов эффективность вычислений остаётся практически неизменяемой, очень медленно снижается. Это объясняется тем, что некоторые отрезки интегрирования имеют большую трудоёмкость из-за негладкости функции. Процессы, которым достались такие отрезки, и определяют время работы всей программы.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19287</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19287"/>
		<updated>2016-11-27T18:08:33Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt; log(n) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность))&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессоров. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессоров. При таком алгоритме мало затрат на обмен данными между процессорами, поэтому было решено воспользоваться только средствами библиотеки MPI.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На графиках, представленных ниже, отображена [[Глоссарий#Производительность|производительность]]  и [[Глоссарий#Эффективность распараллеливания|эффективность распараллеливания]] данного алгоритма в зависимости от изменяемых параметров запуска - точности вычислений и числа процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19286</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19286"/>
		<updated>2016-11-27T18:05:19Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Входные и выходные данные алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt; log(n) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность))&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция может иметь любой вид представления, однако это представление &amp;quot;константно&amp;quot;, то есть можно считать, что объём равен &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt;. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) = O\left( 1 \right) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19285</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19285"/>
		<updated>2016-11-27T18:03:14Z</updated>

		<summary type="html">&lt;p&gt;Stalles: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt; log(n) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность))&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt; O\left( 1 \right) &amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число)&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Формально оценить объём входных данных не получится, так как функция может иметь любой вид представления. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19284</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19284"/>
		<updated>2016-11-27T18:00:42Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt; log(n) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность)&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Формально оценить объём входных данных не получится, так как функция может иметь любой вид представления. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19283</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19283"/>
		<updated>2016-11-27T17:59:23Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt; log(n) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность)&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Формально оценить объём входных данных не получится, так как функция может иметь любой вид представления. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:myefficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19282</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19282"/>
		<updated>2016-11-27T17:58:53Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt; log(n) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность)&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Формально оценить объём входных данных не получится, так как функция может иметь любой вид представления. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:efficiency.png|thumb|center|800px|Рис.4. Эффективность алгоритма в зависимости от числа процессоров и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19281</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19281"/>
		<updated>2016-11-27T17:17:53Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt; log(n) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность)&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма обработки отрезка. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Формально оценить объём входных данных не получится, так как функция может иметь любой вид представления. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19280</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19280"/>
		<updated>2016-11-27T17:15:54Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt; log(n) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность)&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Формально оценить объём входных данных не получится, так как функция может иметь любой вид представления. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. При таком алгоритме мало затрат на обмен данными между процессами, поэтому было решено воспользоваться только средствами библиотеки MPI.&lt;br /&gt;
&lt;br /&gt;
Для оценки масштабируемости были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19279</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19279"/>
		<updated>2016-11-27T17:13:23Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt; log(n) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность)&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Формально оценить объём входных данных не получится, так как функция может иметь любой вид представления. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a_i, b_i) &amp;lt;/math&amp;gt; для своего отрезка &amp;lt;math&amp;gt; [ a_i, b_i ] &amp;lt;/math&amp;gt; независимо от других процессов. Были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19278</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19278"/>
		<updated>2016-11-27T17:12:29Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt; log(n) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность)&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Формально оценить объём входных данных не получится, так как функция может иметь любой вид представления. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Далее каждый процесс выполнял функцию &amp;lt;math&amp;gt; IntTrap(a, b) &amp;lt;/math&amp;gt;. Были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19277</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19277"/>
		<updated>2016-11-27T17:10:41Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt; log(n) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность)&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Формально оценить объём входных данных не получится, так как функция может иметь любой вид представления. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Для распараллеливания программы был выбран алгоритм геометрического параллелизма. Весь отрезок интегрирования делился на равные части, количество которых было равно количеству процессов. Были произведены запуски на 1, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19276</id>
		<title>Участник:Бобцов Борис/Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки</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:%D0%91%D0%BE%D0%B1%D1%86%D0%BE%D0%B2_%D0%91%D0%BE%D1%80%D0%B8%D1%81/%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=19276"/>
		<updated>2016-11-27T17:07:29Z</updated>

		<summary type="html">&lt;p&gt;Stalles: /* Входные и выходные данные алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Вычисление определенного интеграла с использованием адаптивно сгущающейся сетки&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O\left( n \right) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; - количество подотрезков, на которые разбился исходный отрезок&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt; log(n) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; переменной, &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; числа (границы отрезка интегрирования, точность)&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; число&lt;br /&gt;
}}&lt;br /&gt;
Автор статьи: [[Участник:Stalles|Бобцов Борис]] (группа 619)&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Пусть требуется вычислить определённый интеграл &amp;lt;math&amp;gt;I = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; от непрерывной функции &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;. Если может быть найдена первообразная &amp;lt;math&amp;gt;F(x)&amp;lt;/math&amp;gt; подынтегральной функции, то по формуле Ньютона-Лейбница:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\int_a^b{f(x)dx} = F(b) - F(a). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же первообразная не может быть найдена, то для вычисления интеграла прибегают к приближённым формулам, точность которых может быть сделана сколь угодно большой.&lt;br /&gt;
&lt;br /&gt;
Приближённые методы вычисления определённого интеграла в большинстве случаев основаны на том, что определённый интеграл численно равен площади криволинейной трапеции, ограниченной кривой &amp;lt;math&amp;gt; y =f(x)&amp;lt;/math&amp;gt;, сегментом &amp;lt;math&amp;gt; \left[ a,b \right] &amp;lt;/math&amp;gt; оси &amp;lt;math&amp;gt;Ox&amp;lt;/math&amp;gt; и вертикальными прямыми &amp;lt;math&amp;gt;x = a&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x = b&amp;lt;/math&amp;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;
Будем рассматривать проблему вычисления значения определенного интеграла с некоторой заданной точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I(a,b) = \int_a^b{f(x)dx}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть на отрезке &amp;lt;math&amp;gt; [a,b] &amp;lt;/math&amp;gt; задана равномерная сетка, содержащая &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; узел:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; x_{i} = a + \frac{(b - a)} {n} i,   i = 0,...,n &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда, согласно методу трапеций, можно численно найти определенный интеграл от функции &amp;lt;math&amp;gt; f (x) &amp;lt;/math&amp;gt; на отрезке &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \int_a^b{f(x)dx} = \frac{b - a}{n} \left( \frac{f(x_0)}{2} + \sum^{n-1}_{i=1} {f(x_i)}  + \frac{f(x_n)}{2} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Будем полагать значение &amp;lt;math&amp;gt; I &amp;lt;/math&amp;gt; найденным с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, если выполнено условие:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; |I_{n_1} - I_{n_2}| \leq \epsilon |I_{n_2}|, n_1 &amp;gt; n_2, &amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;  n_1 &amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; n_2 &amp;lt;/math&amp;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;
&amp;lt;math&amp;gt; I(a,b) = IntTrap(a, b, f(a), f(b)) = IntTrap(a, (b - a) / 2, f(a), f((b - a) / 2)) + IntTrap((b - a) / 2, b, f((b - a) / 2), f(b)) = ...  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Однако можно заметить, что координаты концов обрабатываемых отрезков  хранятся в программном стеке процесса &amp;lt;math&amp;gt;IntTrap() &amp;lt;/math&amp;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;
----&lt;br /&gt;
Как уже было замечено, алгоритм представляет собой три основные части, выполняющиеся в цикле. Сначала вычисляются частичные суммы на имеющемся отрезке, затем проверяется точность вычислений и, в случае, если точность недостаточна, вычисляются новые отрезки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:123321.png]]&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Будем считать, что описана функция double f( double x ), возвращающая значение f(x) интегрируемой функции f в точке x.&lt;br /&gt;
&lt;br /&gt;
    double f( double x );&lt;br /&gt;
&lt;br /&gt;
    int sp = 0;       //указатель вершины стека&lt;br /&gt;
&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        double a;&lt;br /&gt;
        double b;&lt;br /&gt;
        double fa;&lt;br /&gt;
        double fb;&lt;br /&gt;
        double sab;&lt;br /&gt;
    } stk[1000];      //массив структур, в которых хранятся отложенные значения. 1000 может быть заменена на необходимое число&lt;br /&gt;
&lt;br /&gt;
    //операции доступа к данным стека&lt;br /&gt;
    #define STACK_IS_NOT_FREE ( sp &amp;gt; 0 )&lt;br /&gt;
&lt;br /&gt;
    void PUT_INTO_STACK( double a, double b, double fa, double fb, double sab )&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].sab = sab;&lt;br /&gt;
        ++sp;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    void GET_FROM_STACK( double&amp;amp; a, double&amp;amp; b, double&amp;amp; fa, double&amp;amp; fb, double&amp;amp; sab )&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;
        sab = stk[sp].sab;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //основная функция &lt;br /&gt;
    int IntTrap(a, b)&lt;br /&gt;
    {&lt;br /&gt;
        double I = 0;        //значение интеграла&lt;br /&gt;
        double fa = f( a );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fb = f( b );  //значение функции &amp;lt;math&amp;gt;y = f(x)&amp;lt;/math&amp;gt; в точке &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;&lt;br /&gt;
        double fc = 0;&lt;br /&gt;
        double c = 0;        //середина отрезка интегрирования&lt;br /&gt;
        double sab = 0;      //&lt;br /&gt;
        double sac = 0;      //&lt;br /&gt;
        double scb = 0;      //&lt;br /&gt;
        double sabc = 0;     //переменные для подсчёта текущих сумм&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;
            sac = ( fa + fc ) * ( c - a ) / 2;&lt;br /&gt;
            scb = ( fc + fb ) * ( b - c ) / 2;&lt;br /&gt;
            sabc = sac + scb;&lt;br /&gt;
            if( abs( sab - sabc ) &amp;gt; &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sabc ) ) //&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; - заданная точность&lt;br /&gt;
            {&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;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                I += sabc;&lt;br /&gt;
                if( STACK_IS_NOT_FREE )&lt;br /&gt;
                {&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                GET_FROM_STACK( a, b, fa, fb, sab );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return I;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Работа со стеком происходит через операции доступа к данным. В основной функции в цикле происходит вычисление частичных сумм sac и scb, затем проверяется точность if( abs( sab - sacb ) &amp;gt;&amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; * abs ( sacb ) ). В случае достижения необходимой точности частичные суммы добавляются к значению интеграла I, если отрезков не осталось, то программа завершает работу, иначе из стека выгружается следующий отрезок. Если точность не достигнута, то правый полуотрезок загружается в стек, а левый полуотрезок устанавливается как текущий для следующей итерации цикла.&lt;br /&gt;
&lt;br /&gt;
Когда в стеке не останется отрезков, это будет означать, что программа рассчитала все отрезки до точки a включительно, и возвращает значение I как приближённое значение интеграла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
Пусть для работы алгоритма было необходимо разбить отрезок &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; на сегменты &amp;lt;math&amp;gt; \left[a=x_0 , x_1\right], \left[a=x_1 , x_2\right],... \left[a=x_{n-1} , x_n = b\right] &amp;lt;/math&amp;gt;.&lt;br /&gt;
Так как каждый раз, когда нам приходится вычислять площадь трапеции на более коротком сегменте, мы делим отрезок пополам, получаем, что длина каждого отрезка равна &amp;lt;math&amp;gt; \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^k} &amp;lt;/math&amp;gt; для некоторого натурального &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Назовём глубиной метода максимальное &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; из всех &amp;lt;math&amp;gt; k_i : \left( x_i - x_{i - 1} \right) = \frac{b - a}{2^{k_i}} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Очевидно, что для каждой функции &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и для каждой точности &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt; глубина алгоритма будет разной. То есть можно считать, что &amp;lt;math&amp;gt; k = k(f(x), \epsilon). &amp;lt;/math&amp;gt; Также понятно, что, если &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;- глубина алгоритма при фиксированных &amp;lt;math&amp;gt; y &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;, то максимально возможное количество полученных отрезков, для которых считалась частичная сумма, и количество точек на отрезке &amp;lt;math&amp;gt; \left[a, b\right] &amp;lt;/math&amp;gt; равны соответственно &amp;lt;math&amp;gt; 2^k &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; 2^k + 1 &amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k_i = k, i = 1,2,...,n &amp;lt;/math&amp;gt;. В этом худшем с точки зрения количества операций случае алгоритм прокрутит цикл while( 1 ) &amp;lt;math&amp;gt; 1 + 2 + 4 + ... + 2^k = (2^{k + 1}-1) &amp;lt;/math&amp;gt;   раз.&lt;br /&gt;
&lt;br /&gt;
Если не учитывать при расчёте сложности операции работы с памятью, а только сложения/вычитания и умножения*деления, получим, что за каждый выполненный цикл while( 1 ) алгоритм выполняет 7 сложений и 6 умножений. Итого получаем в худшем случае &amp;lt;math&amp;gt; 7*2^k &amp;lt;/math&amp;gt; сложений и &amp;lt;math&amp;gt; 6*2^k &amp;lt;/math&amp;gt; умножений. Кроме этого, алгоритм посчитает значение &amp;lt;math&amp;gt; f(x) &amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt; n+1 &amp;lt;/math&amp;gt; точках. При этом понятно, что &amp;lt;math&amp;gt; k \leq log(n). &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Получаем, что при разбиении исходного отрезка на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подотрезков получаем глубину алгоритма &amp;lt;math&amp;gt; k \leq log(n) &amp;lt;/math&amp;gt; и  сложность алгоритма &amp;lt;math&amp;gt; O\left( n + 2^{log n} \right) = O\left( n + n \right) = O\left( n  \right) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При этом из всего вышесказанного сразу видно, что при достаточном количестве вычислительных ресурсов распараллеливания высота ярусно-параллельной формы параллельной программы будет равна глубине &amp;lt;math&amp;gt; k \leq log(n), &amp;lt;/math&amp;gt;, а ширина будет не больше, чем количество отрезков &amp;lt;math&amp;gt; n. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
----&lt;br /&gt;
[[Файл:Macromy.png|thumb|center|400px|Рис.1. Общая схема алгоритма ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.1 изображена макроструктура алгоритма. На вход алгоритма подаётся функция, границы отрезка интегрирования и нужная точность. В предварительных вычислениях задаются границы первого отрезка и значения функции в них. Далее запускается операция обработки отрезка, в случае недостаточной точности повторяющаяся для двух подотрезков (и так далее, пока не будет достигнута нужная точность), на выход алгоритм выдаёт приближённое значение интеграла на заданном отрезке с необходимой точностью.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим процедуру обработки отрезка подробнее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png граф.png|thumb|center|800px|Рис.2. Информационный граф одной итерации цикла (обработки одного отрезка) ]]&lt;br /&gt;
&lt;br /&gt;
На Рис.2 изображён информационный граф алгоритма. Синими прямоугольниками изображены входные данные итерации цикла, внутри подписаны имена данных. Кругами изображены операции вычисления переменных, имена которых указаны внутри кругов. Зелёными овалами изображены прочие операции, выполняемые алгоритмом. После операции проверки точности алгоритм загружает правый отрезок в стек, а левый настраивает на дальнейшую обработку, если точность не достигнута, иначе загружает новый отрезок из стека. Затем происходит суммирование текущей частичной суммы с полученными ранее. При отсутствии отрезков в стеке итерация завершается.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Представленный алгоритм обладает хорошим ресурсом параллелизма. В самом деле, пусть в нашем распоряжении есть доступный всем параллельным процессам список отрезков интегрирования, организованный в виде стека. Назовём его глобальным стеком. Предположим, что перед запуском параллельных процессов в глобальный стек помещается единственная запись - координаты концов отрезка интегрирования - в дальнейшем - &amp;quot;отрезок&amp;quot;. Тогда возможно реализовать следующую схему алгоритма, выполняемую каждым из параллельных процессов:&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;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: &lt;br /&gt;
* функция &amp;lt;math&amp;gt; y = f(x)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* точность &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;;&lt;br /&gt;
* границы отрезка интегрирования &amp;lt;math&amp;gt; a, b.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Формально оценить объём входных данных не получится, так как функция может иметь любой вид представления. Кроме функции, на вход подаётся три вещественных числа, их объём - &amp;lt;math&amp;gt; 3 * sizeof( double ) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &lt;br /&gt;
* число &amp;lt;math&amp;gt;I &amp;lt;/math&amp;gt;, приближённо равное &amp;lt;math&amp;gt; \int_a^b{f(x)dx}&amp;lt;/math&amp;gt; с точностью &amp;lt;math&amp;gt; \epsilon &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На выход алгоритм выдаёт одно вещественное число, его объём - &amp;lt;math&amp;gt; sizeof( double ) &amp;lt;/math&amp;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;
Соотношение последовательной и параллельной сложности алгоритма высоко и равно приблизительно &amp;lt;math&amp;gt; \frac{n}{log(n)} &amp;lt;/math&amp;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, 2, 4, 8, 16, 32, 48, 64, 80, 96, 112 и 128 процессах. Кроме того, для каждого количества процессов были произведены запуски при разной точности вычисления интеграла, от &amp;lt;math&amp;gt;10^{-5} &amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;10^{-7} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Png_mapoftime.png|thumb|center|800px|Рис.3. Время работы алгоритма в зависимости от числа процессов и точности вычислений ]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент реализация алгоритма локального стека была указана только в книге Якобовского М.В., Кульковой Е.Ю. &amp;quot;Решение задач на многопроцессорных вычислительных системах с разделяемой памятью.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
# Шнейдер В. Е. и др. Краткий курс высшей математики. Учеб. пособие для втузов. М., «Высш. школа», 1972. 640 с.&lt;br /&gt;
# Якобовский М.В., Кулькова Е.Ю. Решение задач на многопроцессорных вычислительных системах с разделяемой памятью. - М.: СТАНКИН, 2004. – 30 c.&lt;/div&gt;</summary>
		<author><name>Stalles</name></author>
	</entry>
</feed>