<?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=Apaokin</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=Apaokin"/>
	<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/Apaokin"/>
	<updated>2026-10-22T15:44:56Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Sitenotice&amp;diff=30039</id>
		<title>MediaWiki:Sitenotice</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Sitenotice&amp;diff=30039"/>
		<updated>2025-11-21T18:48:56Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: Полностью удалено содержимое страницы&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Sitenotice&amp;diff=30022</id>
		<title>MediaWiki:Sitenotice</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Sitenotice&amp;diff=30022"/>
		<updated>2025-11-18T10:55:08Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;p style=&amp;quot;font-size: 20px;&amp;quot; class=&amp;quot;text-danger&amp;quot;&amp;gt;В связи с обновлением сайта все изменения, сделанные участниками с 21 ноября 15 часов до конца обновления, не будут включены в новую версию. AlgoWiki будет снова функционировать в обычном режиме после исчезновения данного сообщения, скорее всего, в субботу,22 ноября.&amp;lt;/p&amp;gt;&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Sitenotice&amp;diff=30021</id>
		<title>MediaWiki:Sitenotice</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Sitenotice&amp;diff=30021"/>
		<updated>2025-11-18T10:49:43Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;p style=&amp;quot;font-size: 20px;&amp;quot; class=&amp;quot;text-danger&amp;quot;&amp;gt;В связи с обновлением сайта все изменения, сделанные участниками с 21 ноября 15 часов до конца обновления, не будут включены в новую версию. AlgoWiki будет снова функционировать в обычном режиме после исчезновения данного сообщения, скорее всего, в субботу.&amp;lt;/p&amp;gt;&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Sitenotice&amp;diff=30020</id>
		<title>MediaWiki:Sitenotice</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Sitenotice&amp;diff=30020"/>
		<updated>2025-11-18T10:48:32Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: Новая страница: «&amp;lt;p style=&amp;quot;font-size: 20px;&amp;quot; class=&amp;quot;text-danger&amp;quot;&amp;gt;В связи с переездом проекта, все изменения, сделанные участник...»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;p style=&amp;quot;font-size: 20px;&amp;quot; class=&amp;quot;text-danger&amp;quot;&amp;gt;В связи с переездом проекта, все изменения, сделанные участниками с 21 ноября 15 часов до конца переезда, не будут включены в новую версию. Algowiki будет снова функционировать в обычном режиме после исчезновения данного сообщения, скорее всего, в субботу.&amp;lt;/p&amp;gt;&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D0%B2%D0%B8%D0%BA%D0%B8:%D0%A2%D0%B5%D1%81%D1%82&amp;diff=29958</id>
		<title>Алговики:Тест</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D0%B2%D0%B8%D0%BA%D0%B8:%D0%A2%D0%B5%D1%81%D1%82&amp;diff=29958"/>
		<updated>2025-11-06T04:19:21Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: Новая страница: «тест»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;тест&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=28815</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=28815"/>
		<updated>2022-06-07T15:25:48Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* Размещённый здесь код JavaScript будет загружаться пользователям при обращении к каждой странице */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
  var google_tag = &#039;&amp;lt;!-- Global site tag (gtag.js) - Google Analytics --&amp;gt; &amp;lt;script async src=&amp;quot;https://www.googletagmanager.com/gtag/js?id=UA-60413267-1&amp;quot;&amp;gt;&amp;lt;/script&amp;gt; &amp;lt;script&amp;gt;     window.dataLayer = window.dataLayer || [];     function gtag(){dataLayer.push(arguments);} gtag(&amp;quot;js&amp;quot;, new Date()); gtag(&amp;quot;config&amp;quot;, &amp;quot;UA-60413267-1&amp;quot;);  &amp;lt;/script&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
$(&#039;head&#039;).prepend(google_tag);&lt;br /&gt;
&lt;br /&gt;
$(&#039;head&#039;).append(&amp;quot;&amp;lt;script type=&#039;text/javascript&#039; src=&#039;http://www.x3dom.org/download/x3dom.js&#039;&amp;gt; &amp;lt;/script&amp;gt; &amp;lt;link rel=&#039;stylesheet&#039; type=&#039;text/css&#039; href=&#039;http://www.x3dom.org/download/x3dom.css&#039;&amp;gt;&amp;lt;/link&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// $(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
$(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;1 0 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;transform translation=&#039;-3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 1 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;cone&amp;gt;&amp;lt;/cone&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;transform translation=&#039;3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 0 1&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;sphere&amp;gt;&amp;lt;/sphere&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=28814</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=28814"/>
		<updated>2022-06-07T15:16:12Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* Размещённый здесь код JavaScript будет загружаться пользователям при обращении к каждой странице */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
var google_tag = &#039;&amp;lt;!-- Global site tag (gtag.js) - Google Analytics --&amp;gt;&lt;br /&gt;
&amp;lt;script async src=&amp;quot;https://www.googletagmanager.com/gtag/js?id=UA-60413267-1&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
  window.dataLayer = window.dataLayer || [];&lt;br /&gt;
  function gtag(){dataLayer.push(arguments);}&lt;br /&gt;
  gtag(&amp;quot;js&amp;quot;, new Date());&lt;br /&gt;
&lt;br /&gt;
  gtag(&amp;quot;config&amp;quot;, &amp;quot;UA-60413267-1&amp;quot;);&lt;br /&gt;
&amp;lt;/script&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
$(&#039;head&#039;).prepend(google_tag);&lt;br /&gt;
&lt;br /&gt;
$(&#039;head&#039;).append(&amp;quot;&amp;lt;script type=&#039;text/javascript&#039; src=&#039;http://www.x3dom.org/download/x3dom.js&#039;&amp;gt; &amp;lt;/script&amp;gt; &amp;lt;link rel=&#039;stylesheet&#039; type=&#039;text/css&#039; href=&#039;http://www.x3dom.org/download/x3dom.css&#039;&amp;gt;&amp;lt;/link&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// $(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
$(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;1 0 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;transform translation=&#039;-3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 1 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;cone&amp;gt;&amp;lt;/cone&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;transform translation=&#039;3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 0 1&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;sphere&amp;gt;&amp;lt;/sphere&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=28813</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=28813"/>
		<updated>2022-06-07T15:11:26Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* Размещённый здесь код JavaScript будет загружаться пользователям при обращении к каждой странице */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
var google_tag = `&amp;lt;!-- Global site tag (gtag.js) - Google Analytics --&amp;gt;&lt;br /&gt;
&amp;lt;script async src=&amp;quot;https://www.googletagmanager.com/gtag/js?id=UA-60413267-1&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
  window.dataLayer = window.dataLayer || [];&lt;br /&gt;
  function gtag(){dataLayer.push(arguments);}&lt;br /&gt;
  gtag(&#039;js&#039;, new Date());&lt;br /&gt;
&lt;br /&gt;
  gtag(&#039;config&#039;, &#039;UA-60413267-1&#039;);&lt;br /&gt;
&amp;lt;/script&amp;gt;`;&lt;br /&gt;
&lt;br /&gt;
$(&#039;head&#039;).prepend(google_tag);&lt;br /&gt;
&lt;br /&gt;
$(&#039;head&#039;).append(&amp;quot;&amp;lt;script type=&#039;text/javascript&#039; src=&#039;http://www.x3dom.org/download/x3dom.js&#039;&amp;gt; &amp;lt;/script&amp;gt; &amp;lt;link rel=&#039;stylesheet&#039; type=&#039;text/css&#039; href=&#039;http://www.x3dom.org/download/x3dom.css&#039;&amp;gt;&amp;lt;/link&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// $(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
$(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;1 0 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;transform translation=&#039;-3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 1 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;cone&amp;gt;&amp;lt;/cone&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;transform translation=&#039;3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 0 1&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;sphere&amp;gt;&amp;lt;/sphere&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=28812</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=28812"/>
		<updated>2022-06-07T15:09:50Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* Размещённый здесь код JavaScript будет загружаться пользователям при обращении к каждой странице */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
var google_tag = &amp;quot;&amp;lt;!-- Global site tag (gtag.js) - Google Analytics --&amp;gt;&lt;br /&gt;
&amp;lt;script async src=&amp;quot;https://www.googletagmanager.com/gtag/js?id=UA-60413267-1&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
  window.dataLayer = window.dataLayer || [];&lt;br /&gt;
  function gtag(){dataLayer.push(arguments);}&lt;br /&gt;
  gtag(&#039;js&#039;, new Date());&lt;br /&gt;
&lt;br /&gt;
  gtag(&#039;config&#039;, &#039;UA-60413267-1&#039;);&lt;br /&gt;
&amp;lt;/script&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
$(&#039;head&#039;).prepend(google_tag);&lt;br /&gt;
&lt;br /&gt;
$(&#039;head&#039;).append(&amp;quot;&amp;lt;script type=&#039;text/javascript&#039; src=&#039;http://www.x3dom.org/download/x3dom.js&#039;&amp;gt; &amp;lt;/script&amp;gt; &amp;lt;link rel=&#039;stylesheet&#039; type=&#039;text/css&#039; href=&#039;http://www.x3dom.org/download/x3dom.css&#039;&amp;gt;&amp;lt;/link&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// $(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
$(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;1 0 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;transform translation=&#039;-3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 1 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;cone&amp;gt;&amp;lt;/cone&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;transform translation=&#039;3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 0 1&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;sphere&amp;gt;&amp;lt;/sphere&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%BE%D1%82_%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B_(SSSP)&amp;diff=25653</id>
		<title>Поиск кратчайшего пути от одной вершины (SSSP)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%BE%D1%82_%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B_(SSSP)&amp;diff=25653"/>
		<updated>2018-06-19T14:50:07Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-p}}&lt;br /&gt;
== Постановка задачи ==&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Последовательность &amp;lt;math&amp;gt;P(u, v)&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_1 = (u, w_1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e_2 = (w_1, w_2)&amp;lt;/math&amp;gt;, …, &amp;lt;math&amp;gt;e_k = (w_{k-1}, v)&amp;lt;/math&amp;gt; называется &#039;&#039;путём&#039;&#039;, идущим от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к вершине &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Суммарный вес этого пути равен&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f(P(u, v)) = \sum_{i = 1}^k f(e_i).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Требуется для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, достижимой из вершины-источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, указать путь &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt;, имеющий наименьший возможный суммарный вес:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f^*(v) = f(P^*(u, v)) = \min f(P(u, v)).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Название задачи на английском языке – «Single Source Shortest Path» (SSSP).&lt;br /&gt;
&lt;br /&gt;
== Варианты задачи ==&lt;br /&gt;
&lt;br /&gt;
Если требуется найти кратчайшие пути не от одной, а от всех вершин - см. [[Поиск кратчайшего пути для всех пар вершин (APSP)|поиск кратчайшего пути для всех пар вершин]].&lt;br /&gt;
&lt;br /&gt;
Задача может быть поставлена как для ориентированного, так и для неориентированного графа. Приведённая постановка задачи применима в обоих случаях.&lt;br /&gt;
&lt;br /&gt;
В зависимости от ограничений на возможные значения весов различают следующие случаи.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Единичные веса&#039;&#039;&#039;. В этом случае задача существенно упрощается и может быть решена за линейное время [[Поиск в ширину (BFS)|алгоритмом поиска вширь]].&lt;br /&gt;
* &#039;&#039;&#039;Положительные целые веса&#039;&#039;&#039;. Задача также может быть решена за линейное время&amp;lt;ref name=thorup&amp;gt;Thorup, Mikkel. “Undirected Single-Source Shortest Paths with Positive Integer Weights in Linear Time.” Journal of the ACM 46, no. 3 (May 1, 1999): 362–94. doi:10.1145/316542.316548.&amp;lt;/ref&amp;gt; (хотя и с большей константой).&lt;br /&gt;
* &#039;&#039;&#039;Неотрицательные веса&#039;&#039;&#039;. Часто встречающееся на практике ограничение, когда вес ребра соответствует времени или стоимости прохождения участка маршрута и не может быть отрицательным.&lt;br /&gt;
* &#039;&#039;&#039;Произвольные веса&#039;&#039;&#039;. Наиболее общая постановка. Граф не должен содержать циклов с отрицательным суммарным весом, иначе кратчайшего пути не существует.&lt;br /&gt;
&lt;br /&gt;
== Свойства задачи ==&lt;br /&gt;
&lt;br /&gt;
Решение задачи удовлетворяет принципу оптимальности: если путь &amp;lt;math&amp;gt;P^*(u, w)&amp;lt;/math&amp;gt; является частью кратчайшего пути &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;P^*(u, w)&amp;lt;/math&amp;gt; является кратчайшим путём от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Принцип оптимальности означает, что для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; вместо всего кратчайшего пути &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt; достаточно хранить его последнее ребро &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt;. Отсюда следует оценка объёма памяти &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Описание входных и выходных данных ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Преобразование выходных данных и поиск кратчайшего пути ===&lt;br /&gt;
&lt;br /&gt;
Кратчайший путь от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; может быть восстановлен за время &amp;lt;math&amp;gt;O(\left |P^*(u, v) \right |)&amp;lt;/math&amp;gt;, если, начиная с вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, проходить рёбра &amp;lt;math&amp;gt;e^*_w&amp;lt;/math&amp;gt; в обратном направлении до тех пор, пока не будет посещена вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рёбра &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt; могут быть восстановлены за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; перебором рёбер для каждой вершины:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
    e^*_v \in \{ (w, v) \in E \mid f^*(v) = f^*(w) + f((v, w)) \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Перебор может осуществляться параллельно.&lt;br /&gt;
&lt;br /&gt;
Для поиска кратчайших расстояний &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; по набору рёбер &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt;, необходимо построить из них дерево, на котором выполнить [[Поиск в ширину (BFS)|поиск в ширину]] за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (с возможностью параллелизации).&lt;br /&gt;
&lt;br /&gt;
== Алгоритмы решения задачи ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[[Поиск в ширину (BFS)]]&#039;&#039;&#039; для ориентированных невзвешенных графов, сложность &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.  {{#linktop:Поиск в ширину (BFS)}}&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Дейкстры]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1, 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. {{#linktop:Алгоритм Дейкстры}}&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Беллмана-Форда]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; для ориентированных графов с произвольными весами, сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;. {{#linktop:Алгоритм Беллмана-Форда}}&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Δ-шагания]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Meyer, U, and P Sanders. “Δ-Stepping: a Parallelizable Shortest Path Algorithm.” Journal of Algorithms 49, no. 1 (October 2003): 114–52. doi:10.1016/S0196-6774(03)00076-2.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, средняя сложность на графах со случайными весами &amp;lt;math&amp;gt;O(n + m + dL)&amp;lt;/math&amp;gt;, параллельная сложность &amp;lt;math&amp;gt;O((dL + \ln n) \ln n&amp;lt;/math&amp;gt; со средней работой &amp;lt;math&amp;gt;O(n + m + dL \ln n)&amp;lt;/math&amp;gt;.  {{#linktop:Алгоритм Δ-шагания}}&lt;br /&gt;
* &#039;&#039;&#039;Алгоритм Торупа&#039;&#039;&#039;&amp;lt;ref name=thorup /&amp;gt; представляет собой теоретическое доказательство возможности решения задачи для неориентированных графов с положительными целыми весами за линейное время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В ориентированном ациклическом графе с произвольными весами время поиска кратчайших путей линейное – &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;, алгоритм основан на топологической сортировке графа.&lt;br /&gt;
&lt;br /&gt;
Обозначения: &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; – число рёбер, &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; – число вершин, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; – максимальная степень вершины, &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; – максимальный суммарный вес кратчайшего пути.&lt;br /&gt;
&lt;br /&gt;
Перечисленные алгоритмы могут использоваться и для решения [[Поиск кратчайшего пути для всех пар вершин (APSP)|задачи поиска кратчайшего пути для всех пар вершин графа]] (для этого необходимо запустить соответствующий алгоритм для каждой вершины графа, что при необходимости можно сделать параллельно), при этом оценка сложности умножается на &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En: Single Source Shortest Path (SSSP)]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A8%D0%B8%D0%BB%D0%BE%D0%B0%D1%85%D0%B0-%D0%92%D0%B8%D1%88%D0%BA%D0%B8%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0_%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%B5%D0%BD%D1%82_%D1%81%D0%B2%D1%8F%D0%B7%D0%BD%D0%BE%D1%81%D1%82%D0%B8&amp;diff=25652</id>
		<title>Алгоритм Шилоаха-Вишкина поиска компонент связности</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A8%D0%B8%D0%BB%D0%BE%D0%B0%D1%85%D0%B0-%D0%92%D0%B8%D1%88%D0%BA%D0%B8%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0_%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%B5%D0%BD%D1%82_%D1%81%D0%B2%D1%8F%D0%B7%D0%BD%D0%BE%D1%81%D1%82%D0%B8&amp;diff=25652"/>
		<updated>2018-06-19T12:04:55Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &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((m + n) \ln n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(\ln n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n + 2m&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n&amp;lt;/math&amp;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;&amp;lt;ref&amp;gt;Shiloach, Yossi, and Uzi Vishkin. “An &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; Parallel Connectivity Algorithm.” Journal of Algorithms 3, no. 1 (March 1982): 57–67. doi:10.1016/0196-6774(82)90008-6.&amp;lt;/ref&amp;gt; (англ. Shiloach-Vishking Algorithm) предназначен [[Связность в графах|для поиска компонент связности]] неориентированного графа. Используется структура данных, аналогичная [[Система непересекающихся множеств|системе непресекающихся множеств]]. В модели CRCW PRAM алгоритм приписывает каждой вершине идентификатор компоненты связности за время &amp;lt;math&amp;gt;O(\ln n)&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;n + 2m&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;
Последовательная сложность алгоритма Шилоаха-Вишкина &amp;lt;math&amp;gt;O((m + n) \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Время работы алгоритма составляет &amp;lt;math&amp;gt;O(\ln n)&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;n + 2m&amp;lt;/math&amp;gt; процессорах в модели CRCW PRAM.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&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;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;br /&gt;
&lt;br /&gt;
[[en:Shiloach-Vishkin algorithm for finding the connected components]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B0_(MST)&amp;diff=25651</id>
		<title>Построение минимального остовного дерева (MST)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B0_(MST)&amp;diff=25651"/>
		<updated>2018-06-19T11:54:56Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-p}}&lt;br /&gt;
&lt;br /&gt;
== Постановка задачи ==&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Подграф, являющийся деревом и проходящий через все вершины&lt;br /&gt;
&amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, называется &#039;&#039;основным деревом&#039;&#039;. Остовное дерево называется &#039;&#039;минимальным&#039;&#039;, если суммарный вес его рёбер является минимальным среди всех остовных&lt;br /&gt;
деревьев.&lt;br /&gt;
&lt;br /&gt;
Постановка задачи и алгоритм её решения были впервые опубликованы в работе Отакара Борувки&amp;lt;ref name=boruvka&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;. Английское название задачи – Minimum Spanning Tree (MST).&lt;br /&gt;
&lt;br /&gt;
== Варианты задачи ==&lt;br /&gt;
&lt;br /&gt;
Если исходный граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; несвязный, то набор минимальных остовных деревьев для всех компонент связности называется &#039;&#039;минимальным остовным лесом&#039;&#039; (Minimum&lt;br /&gt;
Spanning Forest, MSF).&lt;br /&gt;
&lt;br /&gt;
Для графа с целочисленными весами возможно использование специальных приёмов, таких как [[wikipedia:ru:Поразрядная сортировка|поразрядная сортировка]], что приводит к&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;. Решение задачи зависит не от значений весов, а от их порядка. Таким образом, вместо задания весов можно задать порядок – предикат «меньше или равно» на множестве пар рёбер графа. По этой причине можно без ограничения общности считать, что все веса рёбер различны – для этого следует упорядочить рёбра вначале по весу, а затем по номеру. Кроме того, к исходным весам можно применить любую возрастающую функцию и это не приведёт к изменению решения. Как следствие, можно считать, что все веса находятся в заданном интервале, например, &amp;lt;math&amp;gt;[0, 1]&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Существование и единственность&#039;&#039;&#039;. Минимальный опорный лес всегда существует, а если все веса рёбер различны, то он единственный. Как уже отмечалось, всегда можно сделать веса рёбер различными. Если условие различных весов не выполняется, легко построить пример, в котором будет существовать более одного минимального остовного дерева.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих&lt;br /&gt;
&amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву. На этом свойстве основаны [[алгоритм Борувки]] и [[алгоритм Прима]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Минимальное ребро графа&#039;&#039;&#039;. Если &amp;lt;math&amp;gt;e^*&amp;lt;/math&amp;gt; – единственное ребро графа с минимальным весом, то оно принадлежит минимальному остовному дереву. На этом свойстве основан [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ассоциативность по рёбрам&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;MSF(E)&amp;lt;/math&amp;gt; – минимальный остовный лес графа с рёбрами &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;. Тогда&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        MSF(E_1 \cup E_2 \cup \dots \cup E_k) = MSF(MSF(E_1) \cup MSF(E_2) \cup \dots \cup MSF(E_k)).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Количество рёбер&#039;&#039;&#039; остовного леса для графа с &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершинами и &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; компонентами связности равно &amp;lt;math&amp;gt;n-c&amp;lt;/math&amp;gt;. Это свойство может использоваться для более быстрого завершения работы алгоритмов, если число компонент связности известно заранее.&lt;br /&gt;
&lt;br /&gt;
== Описание входных и выходных данных ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j},&lt;br /&gt;
v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: список рёбер минимального остовного дерева (для несвязного графа – список минимальных остовных деревьев для всех компонент связности).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Алгоритмы решения задачи ==&lt;br /&gt;
&lt;br /&gt;
Существует три классических подхода к решению задачи:&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Борувки]]&#039;&#039;&#039;;&amp;lt;ref name=boruvka /&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; {{#linktop: Алгоритм Борувки}}&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Крускала]]&#039;&#039;&#039;;&amp;lt;ref&amp;gt;Kruskal, Joseph B. “On the Shortest Spanning Subtree of a Graph and the Traveling Salesman Problem.” Proceedings of the American Mathematical Society 7, no. 1 (January 1956): 48–50. doi:10.1090/S0002-9939-1956-0078686-7.&amp;lt;/ref&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Прима]]&#039;&#039;&#039;.&amp;lt;ref&amp;gt;Prim, R C. “Shortest Connection Networks and Some Generalizations.” Bell System Technical Journal 36, no. 6 (November 1957): 1389–1401. doi:10.1002/j.1538-7305.1957.tb01515.x.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt;&lt;br /&gt;
Во всех случаях последовательная сложность алгоритма &amp;lt;math&amp;gt;O(m \ln m)&amp;lt;/math&amp;gt; при использовании обычных структур данных. (Обозначения: &amp;lt;math&amp;gt;m&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;
* &#039;&#039;&#039;[[Алгоритм GHS]]&#039;&#039;&#039; (Gallager, Humblet, Spira)&amp;lt;ref&amp;gt;Gallager, Robert G, P A Humblet, and P M Spira. “A Distributed Algorithm for Minimum-Weight Spanning Trees.” ACM Transactions on Programming Languages and Systems 5, no. 1 (1983): 66–77. doi:10.1145/357195.357200.&amp;lt;/ref&amp;gt; и его последующие версии&amp;lt;ref&amp;gt;Gafni, Eli. “Improvements in the Time Complexity of Two Message-Optimal Election Algorithms,” 175–85, New York, New York, USA: ACM Press, 1985. doi:10.1145/323596.323612.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Awerbuch, B. “Optimal Distributed Algorithms for Minimum Weight Spanning Tree, Counting, Leader Election, and Related Problems,” 230–40, New York, New York, USA: ACM Press, 1987. doi:10.1145/28395.28421.&amp;lt;/ref&amp;gt; являются распределённым вариантом алгоритма Борувки. Это алгоритм обычно используется для автоматического распределённого построения остовного дерева сетью коммутаторов.&lt;br /&gt;
* &#039;&#039;&#039;Алгоритм Габова&#039;&#039;&#039; и др.&amp;lt;ref&amp;gt;Gabow, Harold N, Zvi Galil, Thomas H Spencer, and Robert Endre Tarjan. “Efficient Algorithms for Finding Minimum Spanning Trees in Undirected and Directed Graphs.” Combinatorica 6, no. 2 (June 1986): 109–22. doi:10.1007/BF02579168.&amp;lt;/ref&amp;gt; использует [[wikipedia:ru:Фибоначчиева куча|фибоначчиеву кучу]] для упорядочения рёбер в алгоритме Борувки. Сложность &amp;lt;math&amp;gt;O(m \ln \beta (m, n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Алгоритм Фредмана и Уилларда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Fredman, Michael L, and Dan E Willard. “Trans-Dichotomous Algorithms for Minimum Spanning Trees and Shortest Paths.” Journal of Computer and System Sciences 48, no. 3 (June 1994): 533–51. doi:10.1016/S0022-0000(05)80064-9.&amp;lt;/ref&amp;gt; предназначен для графов с целочисленными весами и имеет линейную оценку сложности &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;. Используется алгоритм Прима в сочетании со специально разработанным алгоритмом кучи ([[wikipedia:AF-heap|AF-heap]]).&lt;br /&gt;
* &#039;&#039;&#039;Алгоритм Каргера&#039;&#039;&#039; и др.&amp;lt;ref&amp;gt;Karger, David R, Philip N Klein, and Robert Endre Tarjan. “A Randomized Linear-Time Algorithm to Find Minimum Spanning Trees.” Journal of the ACM 42, no. 2 (March 1, 1995): 321–28. doi:10.1145/201019.201022.&amp;lt;/ref&amp;gt; решает задачу &#039;&#039;в среднем&#039;&#039; за линейное время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;. (Существование детерминированного алгоритма с линейной оценкой сложности является открытым вопросом.)&lt;br /&gt;
&lt;br /&gt;
Следует отметить, что алгоритмы, имеющие асимптотическую сложность лучшую, чем &amp;lt;math&amp;gt;O(m \ln 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;
== Ссылки ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%BE%D1%82_%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B_(SSSP)&amp;diff=25650</id>
		<title>Поиск кратчайшего пути от одной вершины (SSSP)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%BE%D1%82_%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B_(SSSP)&amp;diff=25650"/>
		<updated>2018-06-19T11:50:26Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-p}}&lt;br /&gt;
== Постановка задачи ==&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Последовательность &amp;lt;math&amp;gt;P(u, v)&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_1 = (u, w_1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e_2 = (w_1, w_2)&amp;lt;/math&amp;gt;, …, &amp;lt;math&amp;gt;e_k = (w_{k-1}, v)&amp;lt;/math&amp;gt; называется &#039;&#039;путём&#039;&#039;, идущим от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к вершине &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Суммарный вес этого пути равен&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f(P(u, v)) = \sum_{i = 1}^k f(e_i).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Требуется для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, достижимой из вершины-источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, указать путь &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt;, имеющий наименьший возможный суммарный вес:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f^*(v) = f(P^*(u, v)) = \min f(P(u, v)).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Название задачи на английском языке – «Single Source Shortest Path» (SSSP).&lt;br /&gt;
&lt;br /&gt;
== Варианты задачи ==&lt;br /&gt;
&lt;br /&gt;
Если требуется найти кратчайшие пути не от одной, а от всех вершин - см. [[Поиск кратчайшего пути для всех пар вершин (APSP)|поиск кратчайшего пути для всех пар вершин]].&lt;br /&gt;
&lt;br /&gt;
Задача может быть поставлена как для ориентированного, так и для неориентированного графа. Приведённая постановка задачи применима в обоих случаях.&lt;br /&gt;
&lt;br /&gt;
В зависимости от ограничений на возможные значения весов различают следующие случаи.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Единичные веса&#039;&#039;&#039;. В этом случае задача существенно упрощается и может быть решена за линейное время [[Поиск в ширину (BFS)|алгоритмом поиска вширь]].&lt;br /&gt;
* &#039;&#039;&#039;Положительные целые веса&#039;&#039;&#039;. Задача также может быть решена за линейное время&amp;lt;ref name=thorup&amp;gt;Thorup, Mikkel. “Undirected Single-Source Shortest Paths with Positive Integer Weights in Linear Time.” Journal of the ACM 46, no. 3 (May 1, 1999): 362–94. doi:10.1145/316542.316548.&amp;lt;/ref&amp;gt; (хотя и с большей константой).&lt;br /&gt;
* &#039;&#039;&#039;Неотрицательные веса&#039;&#039;&#039;. Часто встречающееся на практике ограничение, когда вес ребра соответствует времени или стоимости прохождения участка маршрута и не может быть отрицательным.&lt;br /&gt;
* &#039;&#039;&#039;Произвольные веса&#039;&#039;&#039;. Наиболее общая постановка. Граф не должен содержать циклов с отрицательным суммарным весом, иначе кратчайшего пути не существует.&lt;br /&gt;
&lt;br /&gt;
== Свойства задачи ==&lt;br /&gt;
&lt;br /&gt;
Решение задачи удовлетворяет принципу оптимальности: если путь &amp;lt;math&amp;gt;P^*(u, w)&amp;lt;/math&amp;gt; является частью кратчайшего пути &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;P^*(u, w)&amp;lt;/math&amp;gt; является кратчайшим путём от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Принцип оптимальности означает, что для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; вместо всего кратчайшего пути &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt; достаточно хранить его последнее ребро &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt;. Отсюда следует оценка объёма памяти &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Описание входных и выходных данных ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Преобразование выходных данных и поиск кратчайшего пути ===&lt;br /&gt;
&lt;br /&gt;
Кратчайший путь от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; может быть восстановлен за время &amp;lt;math&amp;gt;O(\left |P^*(u, v) \right |)&amp;lt;/math&amp;gt;, если, начиная с вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, проходить рёбра &amp;lt;math&amp;gt;e^*_w&amp;lt;/math&amp;gt; в обратном направлении до тех пор, пока не будет посещена вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рёбра &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt; могут быть восстановлены за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; перебором рёбер для каждой вершины:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
    e^*_v \in \{ (w, v) \in E \mid f^*(v) = f^*(w) + f((v, w)) \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Перебор может осуществляться параллельно.&lt;br /&gt;
&lt;br /&gt;
Для поиска кратчайших расстояний &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; по набору рёбер &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt;, необходимо построить из них дерево, на котором выполнить [[Поиск в ширину (BFS)|поиск в ширину]] за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (с возможностью параллелизации).&lt;br /&gt;
&lt;br /&gt;
== Алгоритмы решения задачи ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[[Поиск в ширину (BFS)]]&#039;&#039;&#039; для ориентированных невзвешенных графов, сложность &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Дейкстры]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1, 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. {{#linktop:Алгоритм Дейкстры}}&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Беллмана-Форда]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; для ориентированных графов с произвольными весами, сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;. {{#linktop:Алгоритм Беллмана-Форда}}&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Δ-шагания]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Meyer, U, and P Sanders. “Δ-Stepping: a Parallelizable Shortest Path Algorithm.” Journal of Algorithms 49, no. 1 (October 2003): 114–52. doi:10.1016/S0196-6774(03)00076-2.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, средняя сложность на графах со случайными весами &amp;lt;math&amp;gt;O(n + m + dL)&amp;lt;/math&amp;gt;, параллельная сложность &amp;lt;math&amp;gt;O((dL + \ln n) \ln n&amp;lt;/math&amp;gt; со средней работой &amp;lt;math&amp;gt;O(n + m + dL \ln n)&amp;lt;/math&amp;gt;.  {{#linktop:Алгоритм Δ-шагания}}&lt;br /&gt;
* &#039;&#039;&#039;Алгоритм Торупа&#039;&#039;&#039;&amp;lt;ref name=thorup /&amp;gt; представляет собой теоретическое доказательство возможности решения задачи для неориентированных графов с положительными целыми весами за линейное время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В ориентированном ациклическом графе с произвольными весами время поиска кратчайших путей линейное – &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;, алгоритм основан на топологической сортировке графа.&lt;br /&gt;
&lt;br /&gt;
Обозначения: &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; – число рёбер, &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; – число вершин, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; – максимальная степень вершины, &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; – максимальный суммарный вес кратчайшего пути.&lt;br /&gt;
&lt;br /&gt;
Перечисленные алгоритмы могут использоваться и для решения [[Поиск кратчайшего пути для всех пар вершин (APSP)|задачи поиска кратчайшего пути для всех пар вершин графа]] (для этого необходимо запустить соответствующий алгоритм для каждой вершины графа, что при необходимости можно сделать параллельно), при этом оценка сложности умножается на &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En: Single Source Shortest Path (SSSP)]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=25649</id>
		<title>Алгоритм Борувки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=25649"/>
		<updated>2018-06-19T11:42:43Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &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(|E|ln(|V|))&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;max O(ln(|V|)) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(|V| + |E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;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;&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Алгоритм хорошо параллелизуется и является основой для распределённого [[Алгоритм GHS|алгоритма GHS]].&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&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;
Пусть задан связный граф &amp;lt;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;V = ( v_{1}, v_{2}, ..., v_{n} )&amp;lt;/math&amp;gt; и рёбрами &amp;lt;math&amp;gt;E = ( e_{1}, e_{2}, ..., e_{m} )&amp;lt;/math&amp;gt;. Каждому ребру &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt; приписан вес &amp;lt;math&amp;gt;w(e)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Требуется построить дерево &amp;lt;math&amp;gt;T^* \subseteq E&amp;lt;/math&amp;gt;, связывающее все вершины и имеющее наименьший возможный вес среди всех таких деревьев:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; w(T^* )= \min_T( w(T)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где вес множества рёбер есть сумма их весов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;w(T)=\sum_{e \in T} (w(T))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; не является связным, то дерева, связывающего все вершины, не существует. &lt;br /&gt;
&lt;br /&gt;
В этом случае необходимо найти минимальной остовное дерево для каждой компоненты связности &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;. Набор таких деревьев называется минимальным остовным лесом (сокращённо MSF – Minimum Spanning Forest).&lt;br /&gt;
&lt;br /&gt;
==== Вспомогательный алгоритм: система непересекающихся множеств (Union-Find)====&lt;br /&gt;
&lt;br /&gt;
Во всех алгоритмах решения задачи требуется отслеживать, каким уже построенным фрагментам дерева принадлежат те или иные вершины графа. Для этого используется структура данных «система непересекающихся множеств» (Union-Find). Данная структура поддерживает две операции:&lt;br /&gt;
&lt;br /&gt;
1.	&amp;lt;math&amp;gt;FIND(v) = w&amp;lt;/math&amp;gt; – по вершине v возвращает вершину w – «корень» фрагмента, которому принадлежит вершина v. При этом гарантируется, что вершины u и v принадлежат одному и тому же фрагменту, тогда и только тогда, когда &amp;lt;math&amp;gt;FIND(u) = FIND(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; – объединяет два фрагмента, которым принадлежат вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v.&amp;lt;/math&amp;gt; (Если они уже лежат в одном фрагменте, то ничего не происходит.) При практической реализации удобно, чтобы данная операция возвращала значение истина, если объединение фрагментов имело место, и ложь в противном случае.&lt;br /&gt;
&lt;br /&gt;
==== Последовательная версия====&lt;br /&gt;
&lt;br /&gt;
Классический последовательный алгоритм Union-Find описан в статье Тарьяна. Каждой вершине v приписывается указатель на вершину-родителя &amp;lt;math&amp;gt;parent(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
1.	Изначально &amp;lt;math&amp;gt;parent(v) := v&amp;lt;/math&amp;gt; для всех вершин.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; выполняется следующим образом: полагаем &amp;lt;math&amp;gt;u := v&amp;lt;/math&amp;gt;, и далее следуем по указателям &amp;lt;math&amp;gt;u := parent(u)&amp;lt;/math&amp;gt; до тех пор, пока не станет &amp;lt;math&amp;gt;u = parent(u)&amp;lt;/math&amp;gt;. Это и будет результат операции. Дополнительно можно «схлопывать» дерево: присвоить всем посещённым вершинами: &amp;lt;math&amp;gt;parent(u_i) := u&amp;lt;/math&amp;gt;, либо производить схлопывание по пути: &amp;lt;math&amp;gt;parent(u) := parent(parent(u)))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
3.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; выполняется следующим образом: вначале находим корневые вершины &amp;lt;math&amp;gt;u := FIND(u), v := FIND(v)&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;u = v&amp;lt;/math&amp;gt;, то исходные вершины принадлежат одному фрагменту и объединения фрагментов не происходит. В противном случае полагаем одно из &amp;lt;math&amp;gt;parent(u) := v&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;parent(v) := u&amp;lt;/math&amp;gt;. Дополнительно можно отслеживать количество вершин в каждом из фрагментов, чтобы меньший фрагмент подсоединять к большему, а не наоборот (оценки сложности доказываются именно при такой реализации, однако на практике алгоритм хорошо работает и без подсчёта количества вершин).&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
В алгоритме Борувки фрагменты минимального остовного дерева наращиваются постепенно присоединением минимального ребра, выходящего из каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
1.	В самом начале каждая вершина является отдельным фрагментом.&lt;br /&gt;
&lt;br /&gt;
2.	На каждом шаге:&lt;br /&gt;
&lt;br /&gt;
*	Для каждого фрагмента определяется минимальное по весу исходящее ребро.&lt;br /&gt;
&lt;br /&gt;
*	Минимальные рёбра добавляются в минимальное остовное дерево, а соответствующие фрагменты объединяются.&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм останавливается, когда остаётся только один фрагмент либо когда ни у одного из фрагментов нет исходящих рёбер.&lt;br /&gt;
&lt;br /&gt;
Поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента. Таким образом, данную стадию вычислений можно эффективно параллелизовать (в том числе с использованием массового параллелизма графических ускорителей).&lt;br /&gt;
&lt;br /&gt;
Объединение фрагментов также может быть реализовано параллельно с использованием параллельной версии описанного выше алгоритма Union-Find.&lt;br /&gt;
&lt;br /&gt;
Аккуратный подсчёт количества активных фрагментов позволяет остановить алгоритм Борувки на один шаг раньше обычного:&lt;br /&gt;
&lt;br /&gt;
1.	В начале итерации счётчик активных фрагментов обнуляется.&lt;br /&gt;
&lt;br /&gt;
2.	На этапе поиска минимальных рёбер счётчик увеличивается на единицу для каждого фрагмента, у которого были исходящие рёбра.&lt;br /&gt;
&lt;br /&gt;
3.	На этапе объединения фрагментов счётчик уменьшается на единицу каждый раз, когда операция &amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; вернула значение истина.&lt;br /&gt;
&lt;br /&gt;
Если в конце итерации счётчик равен 0 или 1, то вычисления останавливаются.&lt;br /&gt;
Параллелизм возможен на этапе сортировки рёбер по весу, однако основной ход алгоритма является последовательным.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма Борувки для графа с &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; вершинами и &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбрами составляет &amp;lt;math&amp;gt;O(|E| \ln(|V|))&amp;lt;/math&amp;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;: поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента, благодаря чему данную стадию вычислений можно эффективно параллелизовать (как на GPU, так и на CPU). Объединение фрагментов также может быть реализовано параллельно с использованием описанного выше алгоритма Union-Find. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхний уровень параллелизма&#039;&#039;&#039;: построение отдельных минимальных основных деревьев для каждого из списков ребер может производиться параллельно. Например, список ребер может разбиваться на две части, одна из которых обрабатывается на GPU, а вторая параллельно на CPU. &lt;br /&gt;
&lt;br /&gt;
[[file:MST low.png|thumb|center|1000px|Рисунок 1. Информационный граф нижнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы и подробное описание каждого из них. Также можно считать, что на рисунке 1 представлен информационный граф классического алгоритма Борувки, а на рисунке 2 — алгоритма обработки графа.&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма (рисунок 1) расположен на уровнях {3, 4, 5}, соответствующих операциям параллельного поиска минимальных исходящих ребер, а также на уровнях {6, 7, 8}, соответствующих операциям параллельного объединения деревьев. Также  различные операции копирования {1, 2, 8, 9} выполняются параллельно. После выполнения тела цикла производится проверка {12} того, сколько деревьев осталось на текущем шаге, и если данное число не изменилось, то происходит выход из цикла, иначе переход к следующей итерации. &lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма (рисунок 2), как уже говорилось, заключается в параллельном вычислении минимального остовного дерева (compute mst) для различных частей графа. Перед этим производится процесс инициализации (init process), данные которого используют последующие параллельные операции compute mst. Затем, после параллельных вычислений mst, происходит вычисление итогового основного дерева, после чего полученный результат сохраняется (save results).&lt;br /&gt;
&lt;br /&gt;
[[file:MST up.png|thumb|center|1000px|Рисунок 2. Информационный граф верхнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Итак, алгоритм Борувки обладает двумя уровнями параллелизма.&lt;br /&gt;
&lt;br /&gt;
На верхнем уровне минимальные остовные деревья могут искаться для отдельных частей списка ребер графа (параллельные compute_MST на рисунке 2). Однако затем должно последовать финальное объединение полученных ребер и вычисление минимального остовного дерева для полученного графа, которое будет производиться последовательно.&lt;br /&gt;
&lt;br /&gt;
Кроме того, вычисление каждого из минимальных остовных деревьев (параллельные compute_MST на рисунке 2) обладает внутренним ресурсом параллелизма, описанным далее. Операции инициализации и копирования данных ([1], [2], [9]  на рисунке 1) могут производиться параллельно за &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;gt; операций. Также параллельно могут производиться операции поиска минимальных исходящих ребер ([3],[4],[5]), при этом для каждой дуги и обратной к ней независимо, что даёт &amp;lt;math&amp;gt;2*O(|E|)&amp;lt;/math&amp;gt; параллельных операций. Помимо этого, операции объединения деревьев [6], [7], [8] могут также производиться параллельно за &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
В результате для классического алгоритма Борувки ширина ярусно-параллельной формы равна &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;, а высота ЯПФ зависит от числа шагов алгоритма и ограничена сверху &amp;lt;math&amp;gt;O(ln(|V|))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j},&lt;br /&gt;
v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V| + |E|)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: список рёбер минимального остовного дерева (для несвязного графа – список минимальных остовных деревьев для всех компонент связности).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
# Алгоритм останавливается за конечное число шагов, поскольку на каждом шаге становится по крайней мере на один фрагмент меньше.&lt;br /&gt;
# Более того, число фрагментов на каждом шаге уменьшается как минимум вдвое, так что общее число шагов составляет не более &amp;lt;math&amp;gt;\log_2 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;
# Чтение информации о рёбрах. Может производиться последовательно.&lt;br /&gt;
# Проверка принадлежности ребра одному и тому же фрагменту - два чтения массива &amp;lt;math&amp;gt;parent(u)&amp;lt;/math&amp;gt; с вероятным промахом по кэшу.&lt;br /&gt;
# Чтение и обновление минимального веса ребра фрагмента. Данная информация может быть закеширована, особенно на поздних шагах, однако обновление необходимо производить атомарно, что требует инвализации кэша.&lt;br /&gt;
&lt;br /&gt;
На этапе схлопывания фрагментов требуется атомарно обновить массив &amp;lt;math&amp;gt;parent(u)&amp;lt;/math&amp;gt; для каждого добавляемого в MST ребра. В зависимости от реализации параллельной структуры Union-Find корни фрагментов могут находиться ближе к началу массива, что позволяет закешировать эту наиболее часто читаемую область. Требование атомарности, однако, ограничивает эффект от такого кэширования.&lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:boruvka_1.png|thumb|center|700px|Рисунок 3. Алгоритм Борувки. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлен профиль обращений в память для реализации алгоритма Борувки. Этот алгоритм, как и большинство графовых алгоритмов, обладает нерегулярной структурой. Сразу нужно отметить, что локальность реализаций таких алгоритмов во многом зависит от структуры входного графа и может существенно меняться. В данном случае мы рассматриваем лишь один из возможных вариантов.&lt;br /&gt;
&lt;br /&gt;
Можно увидеть, что общий профиль состоит из 4 достаточно схожих этапов (разделены на рис. 3 вертикальными линиями). Однако поскольку этот профиль не обладает регулярной структурой, лучше рассмотреть все этапы.&lt;br /&gt;
&lt;br /&gt;
Начнем с изучения верхней части профиля (фрагмент 1 на рис. 3), которая показана на рис. 4. На каждом этапе большую часть обращений занимает последовательный перебор всех элементов данного фрагмента (выделен на рис. 4 желтым). Остальные обращения на разных этапах устроены по-разному. Если на первом этапе эти обращения разбросаны достаточно далеко друг от друга, что приводит к низкой пространственной и временной локальности, то на последнем этапе почти все обращения (не считая последовательного перебора) выполняются к одному и тому же элементу, что, естественно, характеризуется очень высокой локальностью. Подобное строение всего фрагмента приводит, скорее всего, к средним значениям и по пространственной, и по временной локальности.&lt;br /&gt;
&lt;br /&gt;
[[file:boruvka_2.png|thumb|center|700px|Рисунок 4. Профиль обращений, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее перейдем к изучению фрагмента 2 (рис. 5). Здесь можно увидеть, что строение каждого из 4 этапов отличается достаточно сильно. Как и в случае с фрагментом 1, каждый следующий этап обладает более высокой локальностью, однако здесь это заметно сильнее. При этом отметим, что данный фрагмент задействует всего около 60 элементов, а обращений к ним выполняется достаточно много, так что локальность в данном случае будет высока.&lt;br /&gt;
&lt;br /&gt;
[[file:boruvka_3.png|thumb|center|700px|Рисунок 5. Профиль обращений, фрагмент 2]]&lt;br /&gt;
&lt;br /&gt;
В целом похожая картинка наблюдается и во фрагменте 3. На рис. 6 видны 4 этапа со схожей структурой, и также задействовано около 60 элементов, что позволяет говорить о высокой локальности данного фрагмента.&lt;br /&gt;
&lt;br /&gt;
[[file:boruvka_4.png|thumb|center|700px|Рисунок 6. Профиль обращений, фрагмент 3]]&lt;br /&gt;
&lt;br /&gt;
Отдельное рассмотрение фрагмента 4 (рис. 7) позволяет увидеть, что локальность здесь определяется 4 последовательными переборами всех элементов данного фрагмента. Эти переборы обладают стандартной структурой – шаг по памяти 1, только 1 обращение к каждому элементу; небольшое искривление данных переборов вызвано нерегулярной активностью в других фрагментах, которая приводит к искажению визуального представления профиля. Подобный набор обращений обладает высокой пространственной, но низкой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:boruvka_5.png|thumb|center|500px|Рисунок 7. Профиль обращений, фрагмент 4]]&lt;br /&gt;
&lt;br /&gt;
Таким образом, фрагменты 2 и 3 характеризуются высокой локальностью, другие 2 фрагмента – средней локальностью. А поскольку большая часть обращений приходится именно на фрагменты 2 и 3, можно предположить, что общая локальность должна быть достаточно высока.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 8 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном случае достаточно неплоха – значение daps сравнимо, например, со значением для реализации метода Холецкого. Однако это значение заметно ниже самых производительных реализаций алгоритмов (например, теста Linpack), что в целом неудивительно в случае графовых алгоритмов, традиционно неэффективно работающих с памятью.&lt;br /&gt;
&lt;br /&gt;
[[file:boruvka_daps.png|thumb|center|700px|Рисунок 8. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая алгоритм Борувки, состоит из двух частей: &lt;br /&gt;
&lt;br /&gt;
1. части, отвечающей за общую координацию вычислений&lt;br /&gt;
&lt;br /&gt;
2. части, отвечающей за параллельные вычисления на многоядерных CPU или GPU.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный выше последовательный алгоритм не может применяться в параллельной программе: в реализации &amp;lt;math&amp;gt;MERGE&amp;lt;/math&amp;gt; результаты операций &amp;lt;math&amp;gt;FIND(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; могут постоянно меняться, что приведёт к race condition. Параллельный вариант алгоритма описан в статье.&lt;br /&gt;
&lt;br /&gt;
1.	Каждой вершине v соответствует запись &amp;lt;math&amp;gt;A[v] = { parent, rank }&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;A[v] := { v, 0 }&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	Вспомогательная операция &amp;lt;math&amp;gt;UPDATE(v, rank_v, u, rank_u)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
old := A[v]&lt;br /&gt;
&lt;br /&gt;
if old.parent != v or old.rank != rank_v then return false&lt;br /&gt;
&lt;br /&gt;
new := { u, rank_u }&lt;br /&gt;
&lt;br /&gt;
return CAS(A[v], old, new)&lt;br /&gt;
&lt;br /&gt;
3.	Операция &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    while v != A[v].parent do&lt;br /&gt;
        u := A[v].parent&lt;br /&gt;
        CAS(A[v].parent, u, A[u].parent)&lt;br /&gt;
        v := A[u].parent&lt;br /&gt;
    return v&lt;br /&gt;
&lt;br /&gt;
4.	Операция UNION(u, v):&lt;br /&gt;
&lt;br /&gt;
    while true do&lt;br /&gt;
        (u, v) := (FIND(u), FIND(v))&lt;br /&gt;
        if u = v then return false&lt;br /&gt;
        (rank_u, rank_v) := (A[u].rank, A[v].rank)&lt;br /&gt;
        if (A[u].rank, u) &amp;gt; (A[v].rank, v) then&lt;br /&gt;
            swap((u, rank_u), (v, rank_v))&lt;br /&gt;
        if UPDATE(u, rank_u, v, rank_u) then&lt;br /&gt;
            if rank_u = rank_v then&lt;br /&gt;
                UPDATE(v, rank_v, v, rank_v + 1)&lt;br /&gt;
            return true&lt;br /&gt;
&lt;br /&gt;
Для описанной версии алгоритма гарантируется свойство wait-free. На практике может использоваться упрощённая версия без подсчёта рангов, обладающая более слабым свойством lock-free, но в ряде случаев выигрывающая по скорости.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Возможность обрабатывать фрагменты независимо означает хорошую масштабируемость алгоритма. Сдерживающими факторами являются&lt;br /&gt;
# пропускная способность памяти при чтении данных графа&lt;br /&gt;
# соперничество потоков при выполнении атомарных операций с памятью&lt;br /&gt;
# барьерная синхронизация после каждого подшага алгоритма.&lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Борувки согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости и масштабируемости вширь реализации алгоритма Борувки.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling strong.png|thumb|center|700px|Рисунок 3. Параллельная реализация алгоритма Борувки масштабируемость CPU версии: производительность в зависимости от числа запущенных CPU-потоков.]]&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling wide.png|thumb|center|700px|Рисунок 4. Параллельная реализация алгоритма Борувки масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация алгоритма Борувки, реализованная для CPU.  Все результаты получены на суперкомпьютере «Ломоносов-2». Использовались процессоры Intel Xeon E5-2697v3, задача решалась для графа большого размера на одном узле. &lt;br /&gt;
На рисунках показана эффективность реализации алгоритма Борувки, запуск проводился на 1 узле для графа 2^27, выполнялась 1 итерация.&lt;br /&gt;
[[file:Boruvka cpu.png|thumb|center|700px|Рисунок 11. График загрузки CPU при выполнении алгоритма Борувки]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы не загружены и средний уровень загрузки составляет около 10%. Это нормальная картина для программ, запущенных c использованием одного ядра в реализации. &lt;br /&gt;
&lt;br /&gt;
[[file:Boruvka Loadavg.png|thumb|center|700px|Рисунок 12. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе алгоритма Борувки]]&lt;br /&gt;
&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно на уровне 2. Это указывает на постоянную загрузку аппаратных ресурсов не более чем 2 процессами, однако их число для узла слишком мало, что с одной стороны может указывать на не очень рациональное использование ресурсов.&lt;br /&gt;
&lt;br /&gt;
 [[file:Boruvka L1.png|thumb|center|700px|Рисунок 13. График кэш-промахов L1 в секунду при работе алгоритма Борувки]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов очень высокое и находится на уровне 40 млн/сек . Интересен факт увеличения числа промахов к концу итерации до уровня в 70 млн/сек.&lt;br /&gt;
&lt;br /&gt;
 [[file:Boruvka L2.png|thumb|center|700px|Рисунок 14. График кэш-промахов L1 в секунду при работе алгоритма Борувки]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов второго уровня видно, что число промахов тоже достаточно высокое и находится на уровне 30 млн/сек . На графике промахов второго уровня факт увеличения числа промахов к концу итерации проявляется более явно и увеличивается до значения в 50млн/сек.&lt;br /&gt;
&lt;br /&gt;
 [[file:Boruvka L3.png|thumb|center|700px|Рисунок 15. График кэш-промахов L3 в секунду при работе алгоритма Борувки]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов последнего уровня видно, что число промахов тоже достаточно большое и составляет около 30 млн/сек по всем узлам. Это указывает на то, что задача очень плохо укладывается в кэш-память, и программа постоянно работает с оперативной памятью, что объясняется очень большим размером использованного графа.&lt;br /&gt;
&lt;br /&gt;
[[file:Boruvka Inf data.png|thumb|center|700px|Рисунок 16. График скорости передачи по сети Infiniband в байт/сек при работе алгоритма Борувки]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно высокая интенсивность использования сети на первом этапе. Это объясняется программной логикой, которая предполагает чтение графа из файла с диска, коммуникации с которым происходят на Ломоносов-2 через выделенную для этих задач сеть Infiniband.&lt;br /&gt;
&lt;br /&gt;
 [[file:Boruvka inf pckts.png|thumb|center|700px|Рисунок 17. График скорости передачи по сети Infiniband в пакетах/сек при работе алгоритма Борувки]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается аналогичная картина очень высокой интенсивности на первом этапе выполнения задачи. Далее сеть почти не используется.&lt;br /&gt;
 &lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала стабильно интенсивно,  однако очень неэффективно использовала память из-за очень большого размера графа.&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают алгоритм Борувки и [[алгоритм Крускала]].&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library] {{Buttonlinkimp|28}}&lt;br /&gt;
* RCC for CPU: {{Buttonlinkimp|15}}&lt;br /&gt;
* RCC for GPU: {{Buttonlinkimp|13}}&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En:Boruvka&#039;s algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A8%D0%B8%D0%BB%D0%BE%D0%B0%D1%85%D0%B0-%D0%92%D0%B8%D1%88%D0%BA%D0%B8%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0_%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%B5%D0%BD%D1%82_%D1%81%D0%B2%D1%8F%D0%B7%D0%BD%D0%BE%D1%81%D1%82%D0%B8&amp;diff=25648</id>
		<title>Алгоритм Шилоаха-Вишкина поиска компонент связности</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A8%D0%B8%D0%BB%D0%BE%D0%B0%D1%85%D0%B0-%D0%92%D0%B8%D1%88%D0%BA%D0%B8%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0_%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%B5%D0%BD%D1%82_%D1%81%D0%B2%D1%8F%D0%B7%D0%BD%D0%BE%D1%81%D1%82%D0%B8&amp;diff=25648"/>
		<updated>2018-06-19T11:39:51Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &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((m + n) \ln n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(\ln n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n + 2m&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n&amp;lt;/math&amp;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;&amp;lt;ref&amp;gt;Shiloach, Yossi, and Uzi Vishkin. “An &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; Parallel Connectivity Algorithm.” Journal of Algorithms 3, no. 1 (March 1982): 57–67. doi:10.1016/0196-6774(82)90008-6.&amp;lt;/ref&amp;gt; (англ. Shiloach-Vishking Algorithm) предназначен [[Связность в графах|для поиска компонент связности]] неориентированного графа. Используется структура данных, аналогичная [[Система непересекающихся множеств|системе непресекающихся множеств]]. В модели CRCW PRAM алгоритм приписывает каждой вершине идентификатор компоненты связности за время &amp;lt;math&amp;gt;O(\ln n)&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;n + 2m&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;
Последовательная сложность алгоритма Шилоаха-Вишкина &amp;lt;math&amp;gt;O((m + n) \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Время работы алгоритма составляет &amp;lt;math&amp;gt;O(\ln n)&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;n + 2m&amp;lt;/math&amp;gt; процессорах в модели CRCW PRAM.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
* Ligra {{Buttonlinkimp|35}}&lt;br /&gt;
* Gap {{Buttonlinkimp|40}} &lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;br /&gt;
&lt;br /&gt;
[[en:Shiloach-Vishkin algorithm for finding the connected components]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=25647</id>
		<title>Алгоритм Борувки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=25647"/>
		<updated>2018-06-19T11:32:47Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &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(|E|ln(|V|))&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;max O(ln(|V|)) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(|V| + |E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;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;&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Алгоритм хорошо параллелизуется и является основой для распределённого [[Алгоритм GHS|алгоритма GHS]].&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&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;
Пусть задан связный граф &amp;lt;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;V = ( v_{1}, v_{2}, ..., v_{n} )&amp;lt;/math&amp;gt; и рёбрами &amp;lt;math&amp;gt;E = ( e_{1}, e_{2}, ..., e_{m} )&amp;lt;/math&amp;gt;. Каждому ребру &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt; приписан вес &amp;lt;math&amp;gt;w(e)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Требуется построить дерево &amp;lt;math&amp;gt;T^* \subseteq E&amp;lt;/math&amp;gt;, связывающее все вершины и имеющее наименьший возможный вес среди всех таких деревьев:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; w(T^* )= \min_T( w(T)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где вес множества рёбер есть сумма их весов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;w(T)=\sum_{e \in T} (w(T))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; не является связным, то дерева, связывающего все вершины, не существует. &lt;br /&gt;
&lt;br /&gt;
В этом случае необходимо найти минимальной остовное дерево для каждой компоненты связности &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;. Набор таких деревьев называется минимальным остовным лесом (сокращённо MSF – Minimum Spanning Forest).&lt;br /&gt;
&lt;br /&gt;
==== Вспомогательный алгоритм: система непересекающихся множеств (Union-Find)====&lt;br /&gt;
&lt;br /&gt;
Во всех алгоритмах решения задачи требуется отслеживать, каким уже построенным фрагментам дерева принадлежат те или иные вершины графа. Для этого используется структура данных «система непересекающихся множеств» (Union-Find). Данная структура поддерживает две операции:&lt;br /&gt;
&lt;br /&gt;
1.	&amp;lt;math&amp;gt;FIND(v) = w&amp;lt;/math&amp;gt; – по вершине v возвращает вершину w – «корень» фрагмента, которому принадлежит вершина v. При этом гарантируется, что вершины u и v принадлежат одному и тому же фрагменту, тогда и только тогда, когда &amp;lt;math&amp;gt;FIND(u) = FIND(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; – объединяет два фрагмента, которым принадлежат вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v.&amp;lt;/math&amp;gt; (Если они уже лежат в одном фрагменте, то ничего не происходит.) При практической реализации удобно, чтобы данная операция возвращала значение истина, если объединение фрагментов имело место, и ложь в противном случае.&lt;br /&gt;
&lt;br /&gt;
==== Последовательная версия====&lt;br /&gt;
&lt;br /&gt;
Классический последовательный алгоритм Union-Find описан в статье Тарьяна. Каждой вершине v приписывается указатель на вершину-родителя &amp;lt;math&amp;gt;parent(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
1.	Изначально &amp;lt;math&amp;gt;parent(v) := v&amp;lt;/math&amp;gt; для всех вершин.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; выполняется следующим образом: полагаем &amp;lt;math&amp;gt;u := v&amp;lt;/math&amp;gt;, и далее следуем по указателям &amp;lt;math&amp;gt;u := parent(u)&amp;lt;/math&amp;gt; до тех пор, пока не станет &amp;lt;math&amp;gt;u = parent(u)&amp;lt;/math&amp;gt;. Это и будет результат операции. Дополнительно можно «схлопывать» дерево: присвоить всем посещённым вершинами: &amp;lt;math&amp;gt;parent(u_i) := u&amp;lt;/math&amp;gt;, либо производить схлопывание по пути: &amp;lt;math&amp;gt;parent(u) := parent(parent(u)))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
3.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; выполняется следующим образом: вначале находим корневые вершины &amp;lt;math&amp;gt;u := FIND(u), v := FIND(v)&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;u = v&amp;lt;/math&amp;gt;, то исходные вершины принадлежат одному фрагменту и объединения фрагментов не происходит. В противном случае полагаем одно из &amp;lt;math&amp;gt;parent(u) := v&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;parent(v) := u&amp;lt;/math&amp;gt;. Дополнительно можно отслеживать количество вершин в каждом из фрагментов, чтобы меньший фрагмент подсоединять к большему, а не наоборот (оценки сложности доказываются именно при такой реализации, однако на практике алгоритм хорошо работает и без подсчёта количества вершин).&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
В алгоритме Борувки фрагменты минимального остовного дерева наращиваются постепенно присоединением минимального ребра, выходящего из каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
1.	В самом начале каждая вершина является отдельным фрагментом.&lt;br /&gt;
&lt;br /&gt;
2.	На каждом шаге:&lt;br /&gt;
&lt;br /&gt;
*	Для каждого фрагмента определяется минимальное по весу исходящее ребро.&lt;br /&gt;
&lt;br /&gt;
*	Минимальные рёбра добавляются в минимальное остовное дерево, а соответствующие фрагменты объединяются.&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм останавливается, когда остаётся только один фрагмент либо когда ни у одного из фрагментов нет исходящих рёбер.&lt;br /&gt;
&lt;br /&gt;
Поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента. Таким образом, данную стадию вычислений можно эффективно параллелизовать (в том числе с использованием массового параллелизма графических ускорителей).&lt;br /&gt;
&lt;br /&gt;
Объединение фрагментов также может быть реализовано параллельно с использованием параллельной версии описанного выше алгоритма Union-Find.&lt;br /&gt;
&lt;br /&gt;
Аккуратный подсчёт количества активных фрагментов позволяет остановить алгоритм Борувки на один шаг раньше обычного:&lt;br /&gt;
&lt;br /&gt;
1.	В начале итерации счётчик активных фрагментов обнуляется.&lt;br /&gt;
&lt;br /&gt;
2.	На этапе поиска минимальных рёбер счётчик увеличивается на единицу для каждого фрагмента, у которого были исходящие рёбра.&lt;br /&gt;
&lt;br /&gt;
3.	На этапе объединения фрагментов счётчик уменьшается на единицу каждый раз, когда операция &amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; вернула значение истина.&lt;br /&gt;
&lt;br /&gt;
Если в конце итерации счётчик равен 0 или 1, то вычисления останавливаются.&lt;br /&gt;
Параллелизм возможен на этапе сортировки рёбер по весу, однако основной ход алгоритма является последовательным.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма Борувки для графа с &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; вершинами и &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбрами составляет &amp;lt;math&amp;gt;O(|E| \ln(|V|))&amp;lt;/math&amp;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;: поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента, благодаря чему данную стадию вычислений можно эффективно параллелизовать (как на GPU, так и на CPU). Объединение фрагментов также может быть реализовано параллельно с использованием описанного выше алгоритма Union-Find. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхний уровень параллелизма&#039;&#039;&#039;: построение отдельных минимальных основных деревьев для каждого из списков ребер может производиться параллельно. Например, список ребер может разбиваться на две части, одна из которых обрабатывается на GPU, а вторая параллельно на CPU. &lt;br /&gt;
&lt;br /&gt;
[[file:MST low.png|thumb|center|1000px|Рисунок 1. Информационный граф нижнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы и подробное описание каждого из них. Также можно считать, что на рисунке 1 представлен информационный граф классического алгоритма Борувки, а на рисунке 2 — алгоритма обработки графа.&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма (рисунок 1) расположен на уровнях {3, 4, 5}, соответствующих операциям параллельного поиска минимальных исходящих ребер, а также на уровнях {6, 7, 8}, соответствующих операциям параллельного объединения деревьев. Также  различные операции копирования {1, 2, 8, 9} выполняются параллельно. После выполнения тела цикла производится проверка {12} того, сколько деревьев осталось на текущем шаге, и если данное число не изменилось, то происходит выход из цикла, иначе переход к следующей итерации. &lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма (рисунок 2), как уже говорилось, заключается в параллельном вычислении минимального остовного дерева (compute mst) для различных частей графа. Перед этим производится процесс инициализации (init process), данные которого используют последующие параллельные операции compute mst. Затем, после параллельных вычислений mst, происходит вычисление итогового основного дерева, после чего полученный результат сохраняется (save results).&lt;br /&gt;
&lt;br /&gt;
[[file:MST up.png|thumb|center|1000px|Рисунок 2. Информационный граф верхнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Итак, алгоритм Борувки обладает двумя уровнями параллелизма.&lt;br /&gt;
&lt;br /&gt;
На верхнем уровне минимальные остовные деревья могут искаться для отдельных частей списка ребер графа (параллельные compute_MST на рисунке 2). Однако затем должно последовать финальное объединение полученных ребер и вычисление минимального остовного дерева для полученного графа, которое будет производиться последовательно.&lt;br /&gt;
&lt;br /&gt;
Кроме того, вычисление каждого из минимальных остовных деревьев (параллельные compute_MST на рисунке 2) обладает внутренним ресурсом параллелизма, описанным далее. Операции инициализации и копирования данных ([1], [2], [9]  на рисунке 1) могут производиться параллельно за &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;gt; операций. Также параллельно могут производиться операции поиска минимальных исходящих ребер ([3],[4],[5]), при этом для каждой дуги и обратной к ней независимо, что даёт &amp;lt;math&amp;gt;2*O(|E|)&amp;lt;/math&amp;gt; параллельных операций. Помимо этого, операции объединения деревьев [6], [7], [8] могут также производиться параллельно за &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
В результате для классического алгоритма Борувки ширина ярусно-параллельной формы равна &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;, а высота ЯПФ зависит от числа шагов алгоритма и ограничена сверху &amp;lt;math&amp;gt;O(ln(|V|))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j},&lt;br /&gt;
v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V| + |E|)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: список рёбер минимального остовного дерева (для несвязного графа – список минимальных остовных деревьев для всех компонент связности).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
# Алгоритм останавливается за конечное число шагов, поскольку на каждом шаге становится по крайней мере на один фрагмент меньше.&lt;br /&gt;
# Более того, число фрагментов на каждом шаге уменьшается как минимум вдвое, так что общее число шагов составляет не более &amp;lt;math&amp;gt;\log_2 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;
# Чтение информации о рёбрах. Может производиться последовательно.&lt;br /&gt;
# Проверка принадлежности ребра одному и тому же фрагменту - два чтения массива &amp;lt;math&amp;gt;parent(u)&amp;lt;/math&amp;gt; с вероятным промахом по кэшу.&lt;br /&gt;
# Чтение и обновление минимального веса ребра фрагмента. Данная информация может быть закеширована, особенно на поздних шагах, однако обновление необходимо производить атомарно, что требует инвализации кэша.&lt;br /&gt;
&lt;br /&gt;
На этапе схлопывания фрагментов требуется атомарно обновить массив &amp;lt;math&amp;gt;parent(u)&amp;lt;/math&amp;gt; для каждого добавляемого в MST ребра. В зависимости от реализации параллельной структуры Union-Find корни фрагментов могут находиться ближе к началу массива, что позволяет закешировать эту наиболее часто читаемую область. Требование атомарности, однако, ограничивает эффект от такого кэширования.&lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:boruvka_1.png|thumb|center|700px|Рисунок 3. Алгоритм Борувки. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлен профиль обращений в память для реализации алгоритма Борувки. Этот алгоритм, как и большинство графовых алгоритмов, обладает нерегулярной структурой. Сразу нужно отметить, что локальность реализаций таких алгоритмов во многом зависит от структуры входного графа и может существенно меняться. В данном случае мы рассматриваем лишь один из возможных вариантов.&lt;br /&gt;
&lt;br /&gt;
Можно увидеть, что общий профиль состоит из 4 достаточно схожих этапов (разделены на рис. 3 вертикальными линиями). Однако поскольку этот профиль не обладает регулярной структурой, лучше рассмотреть все этапы.&lt;br /&gt;
&lt;br /&gt;
Начнем с изучения верхней части профиля (фрагмент 1 на рис. 3), которая показана на рис. 4. На каждом этапе большую часть обращений занимает последовательный перебор всех элементов данного фрагмента (выделен на рис. 4 желтым). Остальные обращения на разных этапах устроены по-разному. Если на первом этапе эти обращения разбросаны достаточно далеко друг от друга, что приводит к низкой пространственной и временной локальности, то на последнем этапе почти все обращения (не считая последовательного перебора) выполняются к одному и тому же элементу, что, естественно, характеризуется очень высокой локальностью. Подобное строение всего фрагмента приводит, скорее всего, к средним значениям и по пространственной, и по временной локальности.&lt;br /&gt;
&lt;br /&gt;
[[file:boruvka_2.png|thumb|center|700px|Рисунок 4. Профиль обращений, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее перейдем к изучению фрагмента 2 (рис. 5). Здесь можно увидеть, что строение каждого из 4 этапов отличается достаточно сильно. Как и в случае с фрагментом 1, каждый следующий этап обладает более высокой локальностью, однако здесь это заметно сильнее. При этом отметим, что данный фрагмент задействует всего около 60 элементов, а обращений к ним выполняется достаточно много, так что локальность в данном случае будет высока.&lt;br /&gt;
&lt;br /&gt;
[[file:boruvka_3.png|thumb|center|700px|Рисунок 5. Профиль обращений, фрагмент 2]]&lt;br /&gt;
&lt;br /&gt;
В целом похожая картинка наблюдается и во фрагменте 3. На рис. 6 видны 4 этапа со схожей структурой, и также задействовано около 60 элементов, что позволяет говорить о высокой локальности данного фрагмента.&lt;br /&gt;
&lt;br /&gt;
[[file:boruvka_4.png|thumb|center|700px|Рисунок 6. Профиль обращений, фрагмент 3]]&lt;br /&gt;
&lt;br /&gt;
Отдельное рассмотрение фрагмента 4 (рис. 7) позволяет увидеть, что локальность здесь определяется 4 последовательными переборами всех элементов данного фрагмента. Эти переборы обладают стандартной структурой – шаг по памяти 1, только 1 обращение к каждому элементу; небольшое искривление данных переборов вызвано нерегулярной активностью в других фрагментах, которая приводит к искажению визуального представления профиля. Подобный набор обращений обладает высокой пространственной, но низкой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:boruvka_5.png|thumb|center|500px|Рисунок 7. Профиль обращений, фрагмент 4]]&lt;br /&gt;
&lt;br /&gt;
Таким образом, фрагменты 2 и 3 характеризуются высокой локальностью, другие 2 фрагмента – средней локальностью. А поскольку большая часть обращений приходится именно на фрагменты 2 и 3, можно предположить, что общая локальность должна быть достаточно высока.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 8 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном случае достаточно неплоха – значение daps сравнимо, например, со значением для реализации метода Холецкого. Однако это значение заметно ниже самых производительных реализаций алгоритмов (например, теста Linpack), что в целом неудивительно в случае графовых алгоритмов, традиционно неэффективно работающих с памятью.&lt;br /&gt;
&lt;br /&gt;
[[file:boruvka_daps.png|thumb|center|700px|Рисунок 8. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая алгоритм Борувки, состоит из двух частей: &lt;br /&gt;
&lt;br /&gt;
1. части, отвечающей за общую координацию вычислений&lt;br /&gt;
&lt;br /&gt;
2. части, отвечающей за параллельные вычисления на многоядерных CPU или GPU.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный выше последовательный алгоритм не может применяться в параллельной программе: в реализации &amp;lt;math&amp;gt;MERGE&amp;lt;/math&amp;gt; результаты операций &amp;lt;math&amp;gt;FIND(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; могут постоянно меняться, что приведёт к race condition. Параллельный вариант алгоритма описан в статье.&lt;br /&gt;
&lt;br /&gt;
1.	Каждой вершине v соответствует запись &amp;lt;math&amp;gt;A[v] = { parent, rank }&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;A[v] := { v, 0 }&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	Вспомогательная операция &amp;lt;math&amp;gt;UPDATE(v, rank_v, u, rank_u)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
old := A[v]&lt;br /&gt;
&lt;br /&gt;
if old.parent != v or old.rank != rank_v then return false&lt;br /&gt;
&lt;br /&gt;
new := { u, rank_u }&lt;br /&gt;
&lt;br /&gt;
return CAS(A[v], old, new)&lt;br /&gt;
&lt;br /&gt;
3.	Операция &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    while v != A[v].parent do&lt;br /&gt;
        u := A[v].parent&lt;br /&gt;
        CAS(A[v].parent, u, A[u].parent)&lt;br /&gt;
        v := A[u].parent&lt;br /&gt;
    return v&lt;br /&gt;
&lt;br /&gt;
4.	Операция UNION(u, v):&lt;br /&gt;
&lt;br /&gt;
    while true do&lt;br /&gt;
        (u, v) := (FIND(u), FIND(v))&lt;br /&gt;
        if u = v then return false&lt;br /&gt;
        (rank_u, rank_v) := (A[u].rank, A[v].rank)&lt;br /&gt;
        if (A[u].rank, u) &amp;gt; (A[v].rank, v) then&lt;br /&gt;
            swap((u, rank_u), (v, rank_v))&lt;br /&gt;
        if UPDATE(u, rank_u, v, rank_u) then&lt;br /&gt;
            if rank_u = rank_v then&lt;br /&gt;
                UPDATE(v, rank_v, v, rank_v + 1)&lt;br /&gt;
            return true&lt;br /&gt;
&lt;br /&gt;
Для описанной версии алгоритма гарантируется свойство wait-free. На практике может использоваться упрощённая версия без подсчёта рангов, обладающая более слабым свойством lock-free, но в ряде случаев выигрывающая по скорости.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Возможность обрабатывать фрагменты независимо означает хорошую масштабируемость алгоритма. Сдерживающими факторами являются&lt;br /&gt;
# пропускная способность памяти при чтении данных графа&lt;br /&gt;
# соперничество потоков при выполнении атомарных операций с памятью&lt;br /&gt;
# барьерная синхронизация после каждого подшага алгоритма.&lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Борувки согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости и масштабируемости вширь реализации алгоритма Борувки.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling strong.png|thumb|center|700px|Рисунок 3. Параллельная реализация алгоритма Борувки масштабируемость CPU версии: производительность в зависимости от числа запущенных CPU-потоков.]]&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling wide.png|thumb|center|700px|Рисунок 4. Параллельная реализация алгоритма Борувки масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация алгоритма Борувки, реализованная для CPU.  Все результаты получены на суперкомпьютере «Ломоносов-2». Использовались процессоры Intel Xeon E5-2697v3, задача решалась для графа большого размера на одном узле. &lt;br /&gt;
На рисунках показана эффективность реализации алгоритма Борувки, запуск проводился на 1 узле для графа 2^27, выполнялась 1 итерация.&lt;br /&gt;
[[file:Boruvka cpu.png|thumb|center|700px|Рисунок 11. График загрузки CPU при выполнении алгоритма Борувки]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы не загружены и средний уровень загрузки составляет около 10%. Это нормальная картина для программ, запущенных c использованием одного ядра в реализации. &lt;br /&gt;
&lt;br /&gt;
[[file:Boruvka Loadavg.png|thumb|center|700px|Рисунок 12. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе алгоритма Борувки]]&lt;br /&gt;
&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно на уровне 2. Это указывает на постоянную загрузку аппаратных ресурсов не более чем 2 процессами, однако их число для узла слишком мало, что с одной стороны может указывать на не очень рациональное использование ресурсов.&lt;br /&gt;
&lt;br /&gt;
 [[file:Boruvka L1.png|thumb|center|700px|Рисунок 13. График кэш-промахов L1 в секунду при работе алгоритма Борувки]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов очень высокое и находится на уровне 40 млн/сек . Интересен факт увеличения числа промахов к концу итерации до уровня в 70 млн/сек.&lt;br /&gt;
&lt;br /&gt;
 [[file:Boruvka L2.png|thumb|center|700px|Рисунок 14. График кэш-промахов L1 в секунду при работе алгоритма Борувки]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов второго уровня видно, что число промахов тоже достаточно высокое и находится на уровне 30 млн/сек . На графике промахов второго уровня факт увеличения числа промахов к концу итерации проявляется более явно и увеличивается до значения в 50млн/сек.&lt;br /&gt;
&lt;br /&gt;
 [[file:Boruvka L3.png|thumb|center|700px|Рисунок 15. График кэш-промахов L3 в секунду при работе алгоритма Борувки]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов последнего уровня видно, что число промахов тоже достаточно большое и составляет около 30 млн/сек по всем узлам. Это указывает на то, что задача очень плохо укладывается в кэш-память, и программа постоянно работает с оперативной памятью, что объясняется очень большим размером использованного графа.&lt;br /&gt;
&lt;br /&gt;
[[file:Boruvka Inf data.png|thumb|center|700px|Рисунок 16. График скорости передачи по сети Infiniband в байт/сек при работе алгоритма Борувки]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно высокая интенсивность использования сети на первом этапе. Это объясняется программной логикой, которая предполагает чтение графа из файла с диска, коммуникации с которым происходят на Ломоносов-2 через выделенную для этих задач сеть Infiniband.&lt;br /&gt;
&lt;br /&gt;
 [[file:Boruvka inf pckts.png|thumb|center|700px|Рисунок 17. График скорости передачи по сети Infiniband в пакетах/сек при работе алгоритма Борувки]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается аналогичная картина очень высокой интенсивности на первом этапе выполнения задачи. Далее сеть почти не используется.&lt;br /&gt;
 &lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала стабильно интенсивно,  однако очень неэффективно использовала память из-за очень большого размера графа.&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают алгоритм Борувки и [[алгоритм Крускала]].&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library] {{Buttonlinkimp|28}}&lt;br /&gt;
* RCC for CPU {{Buttonlinkimp|15}}&lt;br /&gt;
* RCC for GPU {{Buttonlinkimp|13}}&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En:Boruvka&#039;s algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%B2_%D1%88%D0%B8%D1%80%D0%B8%D0%BD%D1%83_(BFS)&amp;diff=25646</id>
		<title>Поиск в ширину (BFS)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%B2_%D1%88%D0%B8%D1%80%D0%B8%D0%BD%D1%83_(BFS)&amp;diff=25646"/>
		<updated>2018-06-19T11:11:50Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Алгоритм поиска в ширину (BFS)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(|V| + |E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;N/A, \max O(|V|) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;N/A, \max O(|E|) &amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(|V| + |E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Elijah|И.В.Афанасьев]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Поиск в ширину&#039;&#039;&#039; (англ. Breadth-First Search, BFS) позволяет вычислить кратчайшие расстояния (в терминах количества рёбер) от выделенной вершины ориентированного графа до всех остальных вершин, и/или построить корневое направленное дерево, расстояния в котором совпадают с расстояниями в исходном графе. Кроме того, поиск в ширину позволяет решать задачу проверки достижимости (существуют ли пути между вершиной источником и остальными вершинами графа). Впервые алгоритм поиска в ширину описан в работах Мура&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; и Ли&amp;lt;ref&amp;gt;Lee, C Y. “An Algorithm for Path Connections and Its Applications.” IEEE Transactions on Electronic Computers 10, no. 3 (September 1961): 346–65. doi:10.1109/TEC.1961.5219222.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм основан на обходе вершин графа &amp;quot;по слоям&amp;quot;. На каждом шаге есть множество &amp;quot;передовых&amp;quot; вершин, для смежных к которым производится проверка, относятся ли они к еще не посещенным. Все еще не посещенные вершины добавляются в новое множество &amp;quot;передовых&amp;quot; вершин, обрабатываемых на следующем шаге. Изначально в множество &amp;quot;передовых&amp;quot; вершин входит только вершина-источник, от которой и начинается обход.&lt;br /&gt;
&lt;br /&gt;
В последовательном случае алгоритм имеет алгоритмическую сложность &amp;lt;math&amp;gt;O(|V| + |E|)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; - число вершин в графе, &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; - число ребер в графе.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; без весов, и с выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Путем &amp;lt;math&amp;gt;P(u,v)&amp;lt;/math&amp;gt; между вершинами &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; называется множество ребер &amp;lt;math&amp;gt;(u, v_1), (v_1, v_2), ... (v_{n-1}, v)&amp;lt;/math&amp;gt;. Длиной пути &amp;lt;math&amp;gt;d(u,v)&amp;lt;/math&amp;gt; обозначим число ребер в данном пути между вершинами &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Поиск в ширину находит кратчайшие пути &amp;lt;math&amp;gt;d(u,v)&amp;lt;/math&amp;gt; от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до всех остальных вершин графа описанным далее образом.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма расстояние до вершины-источника &amp;lt;math&amp;gt;d(u)=0&amp;lt;/math&amp;gt;, до остальных вершин &amp;lt;math&amp;gt;d(v) = \infty, \forall v \neq u &amp;lt;/math&amp;gt;. Также в начале работы алгоритма инициализируется множество &amp;lt;math&amp;gt;F = \{u\}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее на каждом шаге алгоритма строится множество вершин &amp;lt;math&amp;gt;P = {w} &amp;lt;/math&amp;gt;, таких, что для &amp;lt;math&amp;gt;\forall v \in F \exists (v, w) \in E | d(w) = \infty &amp;lt;/math&amp;gt;, при этом обновляются расстояния &amp;lt;math&amp;gt;d(w)=d(v)+1&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;\forall w \in P &amp;lt;/math&amp;gt;. Затем производится переход на следующий шаг до тех пор, пока &amp;lt;math&amp;gt;P \neq \emptyset&amp;lt;/math&amp;gt;; при этом в начале каждого шага множество F заменяется на P.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительным ядром алгоритма является обход вершин, смежных с выбранной вершиной &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, с последующим добавлением еще не посещенных вершин в множество &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;. Данная операция выполняется на каждом шаге для каждой вершины &amp;lt;math&amp;gt;v \in F&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Структуру можно описать следующим образом:&lt;br /&gt;
&lt;br /&gt;
1.	Инициализация: всем вершинам присваивается предполагаемое расстояние &amp;lt;math&amp;gt;d(v)=\infty&amp;lt;/math&amp;gt;, кроме вершины-источника, для которой &amp;lt;math&amp;gt;d(u)=0&amp;lt;/math&amp;gt; .&lt;br /&gt;
&lt;br /&gt;
2.	Помещение вершины источника &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; в множество &amp;quot;передовых&amp;quot; вершин &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
3.     Обход вершин множества &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
а) Инициализация множества &amp;lt;math&amp;gt;P=\emptyset&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
б) Для каждой вершины &amp;lt;math&amp;gt;v \in F&amp;lt;/math&amp;gt; обход всех вершин &amp;lt;math&amp;gt;w | \exists (v, w)&amp;lt;/math&amp;gt; (смежных с ней), c помещением в множество &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt; таких вершин &amp;lt;math&amp;gt;w |    d(w)=\infty&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
в) Замена множества &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt; и переход на шаг 3 в случае, если множество &amp;lt;math&amp;gt;F \neq \emptyset&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Простейшая версия алгоритма поиск в ширину может быть реализована при помощи очередей на языке C++ следующим образом.&lt;br /&gt;
Код приведен в предположении, что граф хранится в формате сжатого списка смежности: для каждой вершины в массиве vertices_to_edges_ptrs хранятся индекс начала и индекс конца списка смежных с ней вершин из массива dst_ids.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// init distances&lt;br /&gt;
for(int i = 0; i &amp;lt; vertices_count; i++)&lt;br /&gt;
    _result[i] = MAX_INT;&lt;br /&gt;
    &lt;br /&gt;
// init queue and first vertex&lt;br /&gt;
std::queue&amp;lt;int&amp;gt; vertex_queue;&lt;br /&gt;
vertex_queue.push(_source_vertex);&lt;br /&gt;
_result[_source_vertex] = 1;&lt;br /&gt;
    &lt;br /&gt;
// do bfs&lt;br /&gt;
while(vertex_queue.size() &amp;gt; 0)&lt;br /&gt;
{&lt;br /&gt;
    int cur_vertex = vertex_queue.front();&lt;br /&gt;
    vertex_queue.pop();&lt;br /&gt;
        &lt;br /&gt;
    long long first = vertices_to_edges_ptrs[cur_vertex];&lt;br /&gt;
    long long last = vertices_to_edges_ptrs[cur_vertex + 1];&lt;br /&gt;
    for(long long i = first; i &amp;lt; last; i++)&lt;br /&gt;
    {&lt;br /&gt;
        int dst_id = dst_ids[i];&lt;br /&gt;
        if(_result[dst_id] == MAX_INT)&lt;br /&gt;
        {&lt;br /&gt;
            _result[dst_id] = _result[src_id] + 1;&lt;br /&gt;
            vertex_queue.push(dst_id);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм имеет последовательную сложность &amp;lt;math&amp;gt;O(|V| + |E|)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; - число вершин и ребер графа соответственно: алгоритм инициализирует начальный массив расстояний - &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;gt; операций, а затем обходит каждую вершину один единственный раз -  &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt; операций. Данная оценка верна в случае, если формат хранения графа позволяет обходить вершины, смежные к выбранной (к примеру форматы списка смежности, сжатого списка смежности). При использовании других форматов оценка сложности может быть большей.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
Информационный граф классического алгоритма поиска в ширину приведен на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Classical_BFS_ig.png|thumb|center|500px|Рисунок 1. Информационный граф алгоритма BFS.]]&lt;br /&gt;
&lt;br /&gt;
На рисунке 1 используются следующие обозначения: &lt;br /&gt;
&lt;br /&gt;
[1] - добавление вершины-источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к множеству &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
[2] - извлечение добавленной вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; из множества &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[3] - проверка расстояний до вершин, смежных с вершиной &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[4] - добавление еще не посещенных вершин в множество &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[5] - замена множества &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; на  &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt; и проверка его пустоты. В случае непустого множества - переход на следующую итерацию, иначе завершение работы алгоритма.&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм имеет один важный недостаток при реализации: операция [4] требует бесконфликтной возможности добавления элементов в множество P, что, на практике, всегда будет сводиться к сериализации обращений к структуре данных, моделирующей данное множество.&lt;br /&gt;
&lt;br /&gt;
В результате часто используется модификация алгоритма (далее алгоритм-2), использующая набор независимых структур данных для каждого из параллельных процессов. Информационный граф данного подхода приведен на рисунке 2.&lt;br /&gt;
&lt;br /&gt;
[[Файл:BFS_ig_parallel_queues.png|thumb|center|500px|Рисунок 2. Информационный граф алгоритма BFS (независимые структуры данных).]]&lt;br /&gt;
&lt;br /&gt;
Обозначения для рисунка 2:&lt;br /&gt;
&lt;br /&gt;
[1] - добавление вершины-источника в множество &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[2] - разделение данных множества &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; между процессами&lt;br /&gt;
&lt;br /&gt;
[3] - помещение в множества &amp;lt;math&amp;gt;F_i&amp;lt;/math&amp;gt; соответствующих данных из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; каждым процессом с номером i.&lt;br /&gt;
&lt;br /&gt;
[4] - извлечение очередной вершины из множеств &amp;lt;math&amp;gt;F_i&amp;lt;/math&amp;gt;, обход её соседей и добавление их в множество &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt; в случае, если они еще не посещены&lt;br /&gt;
&lt;br /&gt;
[5] - попарное слияние множеств &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt; для различных процессов, итоговое преобразование их в множество &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[6] - проверка условия выхода из цикла&lt;br /&gt;
&lt;br /&gt;
Кроме того, в случае, если реализация структур данных, моделирующих множества &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;, невозможна, может использоваться квадратичный по сложности алгоритм, схожий с алгоритм Беллмана-Форда. Основная идея заключается в том, что на каждом шаге производится обход всех ребер графа с обновлением текущего массива дистанций. Информационный граф данной модификации алгоритма приведен на рисунке 3.&lt;br /&gt;
&lt;br /&gt;
[[Файл:quadratic_BFS_ig.png|thumb|center|500px|Рисунок 3. Информационный граф алгоритма BFS (квадратичный подход к распараллеливанию).]]&lt;br /&gt;
&lt;br /&gt;
Обозначения для рисунка 3:&lt;br /&gt;
&lt;br /&gt;
[1] - инициализация расстояний до вершины-источника&lt;br /&gt;
&lt;br /&gt;
[2] - инициализация расстояний до остальных вершин графа&lt;br /&gt;
&lt;br /&gt;
[3] - загрузка информации об очередном ребре и обновление дистанций до соответствующих вершин. &lt;br /&gt;
&lt;br /&gt;
[4] - проверка условия выхода из цикла&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В ходе работы классический вариант алгоритма обходит граф по слоям. В каждый слой добавляются еще не посещенные вершины, достижимые из вершин предыдущего слоя. Обход вершин каждого слоя, как и их соседей, может производиться параллельно. Точно оценить число вершин в каждом слое невозможно в силу того, что их количество зависит от структуры связанности входного графа. Аналогично невозможно оценить число шагов алгоритма, за которое будут найдены все кратчайшие пути.&lt;br /&gt;
&lt;br /&gt;
Произведем оценку ширины ярусно-параллельной формы алгоритма через максимальное число вершин p в слое среди всех шагов алгоритма. Тогда число параллельных операций на данном слое будет равно сумме числа смежных вершин для каждой вершины слоя: &amp;lt;math&amp;gt;\sum_{n=1}^{p} degree(v_i)&amp;lt;/math&amp;gt;, при этом для каждого слоя данное значение будет различным. Высота ярусно-параллельной формы будет равна числу шагов в алгоритме и может быть оценена только сверху (не более &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
При квадратичном подходе к параллельной реализации алгоритма на каждом шаге выполняется &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt; операций, которые могут быть выполнены параллельно; таким образом, ширина ЯПФ данной модификации алгоритма равна &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;. Число шагов алгоритма, как и в классическом случае, зависит от структуры графа и может быть оценено сверху как &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: граф &amp;lt;math&amp;gt;G(V, E)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt;, вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V| + |E|)&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;v&amp;lt;/math&amp;gt; исходного графа расстояние &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;, определенное как число ребер, лежащих на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа значение достижимости (достижима или нет) от вершины-источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V|)&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;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* Распределённый алгоритм поиска вширь является вычислительным ядром бенчмарка [http://www.graph500.org Graph500].&lt;br /&gt;
** MPI: {{Buttonlinkimp|12}}&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/breadth_first_search.html breadth_first_search]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/breadth_first_visit.html breadth_first_visit]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/breadth_first_search.html breadth_first_search]&amp;lt;/code&amp;gt;).{{Buttonlinkimp|11}}&lt;br /&gt;
* Java: [http://webgraph.di.unimi.it WebGraph] (класс &amp;lt;code&amp;gt;[http://webgraph.di.unimi.it/docs/it/unimi/dsi/webgraph/algo/ParallelBreadthFirstVisit.html ParallelBreadthFirstVisit]&amp;lt;/code&amp;gt;), многопоточная реализация.&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.traversal.breadth_first_search.bfs_edges.html bfs_edges]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python/C++: [https://networkit.iti.kit.edu NetworKit] (класс &amp;lt;code&amp;gt;[https://networkit.iti.kit.edu/data/uploads/docs/NetworKit-Doc/python/html/graph.html#networkit.graph.BFS networkit.graph.BFS]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* RCC for CPU {{Buttonlinkimp|9}}&lt;br /&gt;
* RCC for GPU {{Buttonlinkimp|26}}&lt;br /&gt;
* Gap {{Buttonlinkimp|36}}&lt;br /&gt;
* Litra {{Buttonlinkimp|39}}&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En:Breadth-first search (BFS)]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Buttonlinkimp&amp;diff=25645</id>
		<title>Шаблон:Buttonlinkimp</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Buttonlinkimp&amp;diff=25645"/>
		<updated>2018-06-19T10:59:41Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: Новая страница: «{{Buttonlink|http://top53.parallel.ru/algo_results/implementation/{{{1}}}}}»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Buttonlink|http://top53.parallel.ru/algo_results/implementation/{{{1}}}}}&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Buttonlink&amp;diff=25641</id>
		<title>Шаблон:Buttonlink</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Buttonlink&amp;diff=25641"/>
		<updated>2018-06-18T16:45:31Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[{{{1}}} &amp;lt;span style=&amp;quot;font-size:10px;&amp;quot; class=&amp;quot;mw-ui-button mw-ui-progressive&amp;quot;&amp;gt;Get Perf.Data&amp;lt;/span&amp;gt;]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=25639</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=25639"/>
		<updated>2018-06-15T14:39:38Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* Размещённый здесь код JavaScript будет загружаться пользователям при обращении к каждой странице */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
$(&#039;head&#039;).append(&amp;quot;&amp;lt;script type=&#039;text/javascript&#039; src=&#039;http://www.x3dom.org/download/x3dom.js&#039;&amp;gt; &amp;lt;/script&amp;gt; &amp;lt;link rel=&#039;stylesheet&#039; type=&#039;text/css&#039; href=&#039;http://www.x3dom.org/download/x3dom.css&#039;&amp;gt;&amp;lt;/link&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// $(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
$(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;1 0 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;transform translation=&#039;-3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 1 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;cone&amp;gt;&amp;lt;/cone&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;transform translation=&#039;3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 0 1&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;sphere&amp;gt;&amp;lt;/sphere&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%CE%94-%D1%88%D0%B0%D0%B3%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=25638</id>
		<title>Алгоритм Δ-шагания</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%CE%94-%D1%88%D0%B0%D0%B3%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=25638"/>
		<updated>2018-06-15T14:34:25Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-a}}&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм дельта-шагания&#039;&#039;&#039;&amp;lt;ref&amp;gt;Meyer, U, and P Sanders. “Δ-Stepping: a Parallelizable Shortest Path Algorithm.” Journal of Algorithms 49, no. 1 (October 2003): 114–52. doi:10.1016/S0196-6774(03)00076-2.&amp;lt;/ref&amp;gt; (англ. Δ-Stepping) предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм изначально проектировался с целью эффективной параллелизации.&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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;W&#039;&#039;;&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;;&lt;br /&gt;
   параметр &#039;&#039;Δ&#039;&#039; &amp;gt; 0.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; DeltaStepping(&#039;&#039;V&#039;&#039;, &#039;&#039;E&#039;&#039;, &#039;&#039;W&#039;&#039;, &#039;&#039;u&#039;&#039;, &#039;&#039;Δ&#039;&#039;):&lt;br /&gt;
      &#039;&#039;LightEdges&#039;&#039; := { e ∈ E | W(e) ≤ Δ }&lt;br /&gt;
      &#039;&#039;HeavyEdges&#039;&#039; := { e ∈ E | W(e) &amp;gt; Δ }&lt;br /&gt;
      &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
      Relax(&#039;&#039;u&#039;&#039;, 0)&lt;br /&gt;
      &#039;&#039;&#039;while&#039;&#039;&#039; any({ &#039;&#039;Buckets&#039;&#039;(i) ≠ ∅ }):&lt;br /&gt;
          &#039;&#039;Bucket&#039;&#039; := first({ &#039;&#039;Buckets&#039;&#039;(i) ≠ ∅ })&lt;br /&gt;
          &#039;&#039;Deleted&#039;&#039; := ∅&lt;br /&gt;
          &#039;&#039;&#039;while&#039;&#039;&#039; &#039;&#039;Bucket&#039;&#039; ≠ ∅:&lt;br /&gt;
              &#039;&#039;Requests&#039;&#039; := FindRequests(&#039;&#039;Bucket&#039;&#039;, &#039;&#039;LightEdges&#039;&#039;)&lt;br /&gt;
              &#039;&#039;Deleted&#039;&#039; := &#039;&#039;Deleted&#039;&#039; ∪ &#039;&#039;Bucket&#039;&#039;&lt;br /&gt;
              &#039;&#039;Bucket&#039;&#039; := ∅&lt;br /&gt;
              RelaxAll(&#039;&#039;Requests&#039;&#039;)&lt;br /&gt;
          RelaxAll(FindRequests(&#039;&#039;Deleted&#039;&#039;, &#039;&#039;HeavyEdges&#039;&#039;))&lt;br /&gt;
          &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; Relax(&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;x&#039;&#039; &amp;lt; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;):&lt;br /&gt;
         &#039;&#039;OldBucket&#039;&#039; := B(⌊&#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) / &#039;&#039;Δ&#039;&#039;⌋)&lt;br /&gt;
         &#039;&#039;NewBucket&#039;&#039; := B(⌊&#039;&#039;x&#039;&#039; / &#039;&#039;Δ&#039;&#039;⌋)&lt;br /&gt;
         &#039;&#039;OldBucket&#039;&#039; := &#039;&#039;OldBucket&#039;&#039; \ {&#039;&#039;v&#039;&#039;}&lt;br /&gt;
         &#039;&#039;NewBucket&#039;&#039; := &#039;&#039;NewBucket&#039;&#039; ∪ {&#039;&#039;v&#039;&#039;}&lt;br /&gt;
         &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;x&#039;&#039;&lt;br /&gt;
         &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; RelaxAll(&#039;&#039;R&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; (&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;) ∈ &#039;&#039;R&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; Relax(&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;)&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; FindRequests(&#039;&#039;V&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;, &#039;&#039;E&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;return&#039;&#039;&#039; { (&#039;&#039;w&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;W&#039;&#039;(&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;)) | &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039; and (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;}&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Средняя последовательная сложность алгоритма на графах со случайными весами &amp;lt;math&amp;gt;O(n + m + dL)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; – максимальный суммарный вес кратчайшего пути, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; – максимальная длина кратчайшего пути.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Средняя время работы параллельного алгоритма на графах со случайными весами на &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; процессорах составляет &amp;lt;math&amp;gt;O((dL + \ln n) \ln n)&amp;lt;/math&amp;gt; со средней работой &amp;lt;math&amp;gt;O(n + m + dL \ln n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Предельные случаи алгоритма Δ-шагания:&lt;br /&gt;
* при &amp;lt;math&amp;gt;\Delta \to 0&amp;lt;/math&amp;gt;: [[алгоритм Дейкстры]];&lt;br /&gt;
* при &amp;lt;math&amp;gt;\Delta \to \infty&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;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#delta-stepping-algorithm delta_stepping_shortest_paths]&amp;lt;/code&amp;gt;). {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/25}}&lt;br /&gt;
* Gap: {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/38}}&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;br /&gt;
&lt;br /&gt;
[[en:Δ-stepping algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=25637</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=25637"/>
		<updated>2018-06-15T14:32:18Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &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(|V||E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;N/A, max O(|V|) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(|V| + |E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(|V|^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(|V||E|)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(|E| + |V|\ln(|V|))&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В самом начале производится присваивание &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;d(v) = \infty&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\forall v \ne u&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Далее происходит &amp;lt;math&amp;gt;|V|-1&amp;lt;/math&amp;gt; итерация, в ходе каждой из которых производится релаксация всех рёбер графа.&lt;br /&gt;
&lt;br /&gt;
Структуру можно описать следующим образом:&lt;br /&gt;
&lt;br /&gt;
1.	Инициализация: всем вершинам присваивается предполагаемое расстояние &amp;lt;math&amp;gt;t(v)=\infty&amp;lt;/math&amp;gt;, кроме вершины-источника, для которой &amp;lt;math&amp;gt;t(u)=0&amp;lt;/math&amp;gt; .&lt;br /&gt;
&lt;br /&gt;
2.	Релаксация множества рёбер &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
а)	Для каждого ребра &amp;lt;math&amp;gt;e=(v,z) \in E&amp;lt;/math&amp;gt; вычисляется новое предполагаемое расстояние &amp;lt;math&amp;gt;t^&#039; (z)=t(v)+ w(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
б)	Если &amp;lt;math&amp;gt;t^&#039; (z)&amp;lt; t(z)&amp;lt;/math&amp;gt;, то происходит присваивание &amp;lt;math&amp;gt;t(z) := t&#039; (z)&amp;lt;/math&amp;gt; (релаксация ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; ).&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм производит релаксацию всех рёбер графа до тех пор, пока на очередной итерации происходит релаксация хотя бы одного ребра.&lt;br /&gt;
&lt;br /&gt;
Если на &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt;-й итерации всё ещё производилась релаксацию рёбер, то в графе присутствует цикл отрицательной длины. Ребро &amp;lt;math&amp;gt;e=(v,z)&amp;lt;/math&amp;gt;, лежащее на таком цикле, может быть найдено проверкой следующего условия (проверяется для всех рёбер за линейное время): &amp;lt;math&amp;gt;t(v)+w(e)&amp;lt;d(z)&amp;lt;/math&amp;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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
 &#039;&#039;d&#039;&#039;(&#039;&#039;u&#039;&#039;) = 0&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;i&#039;&#039; &#039;&#039;&#039;from&#039;&#039;&#039; 1 &#039;&#039;&#039;to&#039;&#039;&#039; |&#039;&#039;V&#039;&#039;| - 1:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;w&#039;&#039;, &#039;&#039;v&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Алгоритм выполняет &amp;lt;math&amp;gt;|V|-1&amp;lt;/math&amp;gt; итерацию, на каждой из которых происходит релаксация &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбер. Таким образом, общий объём работы составляет &amp;lt;math&amp;gt;O(|V||E|)&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 представлен информационный граф алгоритма, демонстрирующий описанные уровни параллелизма.&lt;br /&gt;
&lt;br /&gt;
[[file:APSP.png|thumb|center|700px|Рисунок 1. Информационный граф обобщенного алгоритма Беллмана-Форда.]]&lt;br /&gt;
&lt;br /&gt;
На приведенном далее информационном графе нижний уровень параллелизма обозначен в горизонтальных плоскостях. Множество всех плоскостей представляет собой верхний уровень параллелизма (операции в каждой плоскости могут выполняться параллельно).&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма расположен на уровнях [2] и [3], соответствующим операциям инициализации массива дистанций [2] и обновления массива c использованием данных массива ребер [3]. Операция [4] - проверка того, были ли изменения на последней итерации и выход из цикла, если таковых не было.&lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма, как уже говорилось, заключается в параллельном подсчете дистанций для различных вершин-источников, и на рисунке отмечен разными плоскостями.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм обладает значительным ресурсом параллелизма. Во-первых, поиск кратчайших путей от различных вершин может производиться независимо для каждой из вершин (параллельные вертикальные плоскости на рисунке 1). Во-вторых, поиск кратчайших путей от фиксированной вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; также может выполняться параллельно: инициализация начальных путей [2] требует &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; параллельных операции, релаксация каждого ребра требует &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt; параллельных операции. &lt;br /&gt;
&lt;br /&gt;
Таким образом, при наличии &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt; процессоров алгоритм завершит работу максимум за &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; шагов. В реальности, шагов обычно требуется меньше, а именно &amp;lt;math&amp;gt;O(r)&amp;lt;/math&amp;gt; -(максимальная длина среди всех кратчайших путей от выбранной вершины-источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Таким образом, ширина ярусно-параллельной формы алгоритма равна &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;, высота ЯПФ - &amp;lt;math&amp;gt;O(r) | r &amp;lt; |V|&amp;lt;/math&amp;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;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V| + |E|)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Алгоритм может распознавать наличие отрицательных циклов в графе. Ребро &amp;lt;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(|E|)&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;
[[file:bellman_ford_1.png|thumb|center|700px|Рисунок 2. Алгоритм Беллмана-Форда. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации алгоритма Беллмана-Форда. Первое, что сразу стоит отметить – число обращений в память гораздо больше числа задействованных данных. Это говорит о частом повторном использовании одних и тех же данных, что обычно приводит к высокой временной локальности. Далее, видна явная регулярная структура производимых обращений в память, видны повторяющиеся итерации работы алгоритма. Практически все обращения образуют фрагменты, похожие на последовательный перебор, кроме самой верхней части, где наблюдается более сложная структура.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим более детально отдельные фрагменты общего профиля, чтобы лучше разобраться в его структуре. На рис. 3 представлен фрагмент 1 (выделен на рис. 2), на котором показаны первые 500 обращений в память (отметим, что другой наклон двух последовательных переборов связан с измененным отношением сторон в рассматриваемой области). Данный рисунок показывает, что выделенные желтым части 1 и 2 являются практически идентичными последовательными переборами; отличие между ними только в том, что в части 1 обращения выполняются в два раза чаще, поэтому на рис. 3 эта часть представлена большим числом точек. Как мы знаем, подобные профили характеризуются высокой пространственной и низкой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим более интересный фрагмент 2, отмеченный на рис. 2 (см. рис. 4). Здесь можно снова увидеть подтверждение регулярности обращений в нижней области профиля, однако верхняя область явно устроена гораздо сложнее; хотя и здесь просматривается регулярность. В частности, также видны те же самые итерации, в которых здесь можно выделить большие последовательности обращений к одним и тем же данным. Пример такого поведения, оптимального с точки зрения локальности, выделен на рисунке желтым. &lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_3.png|thumb|center|700px|Рисунок 4. Профиль обращений, фрагмент 2]]&lt;br /&gt;
&lt;br /&gt;
Чтобы понять структуру обращений в память в верхней части, можно рассмотреть ее еще подробнее. Приведем визуализацию небольшой области фрагмента 2, выделенной на рис. 4 зеленым. Однако в данном случае дальнейшее приближение (рис. 5) не привносит большей ясности: видна нерегулярная структура внутри итерации, характер которой достаточно сложно описать. Но в данном случае этого и не требуется – можно заметить, что по вертикали отложено всего 15 элементов, при этом обращений к ним выполняется гораздо больше. Независимо от структуры обращений, такой профиль обладает очень высокой как пространственной, так и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_4.png|thumb|center|700px|Рисунок 5. Небольшая часть фрагмента 2]]&lt;br /&gt;
&lt;br /&gt;
А так как основная масса обращений приходится именно на фрагмент 2, можно утверждать, что и весь общий профиль обладает высокой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что по производительности работы с памятью данная реализация алгоритма показывает очень хорошие результаты. В частности, значение daps сравнимо с оценкой для теста Linpack, который известен высокой эффективностью взаимодействия с подсистемой памяти. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая алгоритм поиска кратчайших путей, состоит из двух частей: части, отвечающей за общую координацию вычислений, а также параллельные вычисления на многоядерных CPU, и GPU части, отвечающей только за вычисления на графическом ускорителе.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Алгоритм обладает значительным потенциалом масштабируемости, так как каждое ребро обрабатывается независимо и можно поручить каждому вычислительному процессу свою часть рёбер графа. Узким местом является доступ к разделяемому всеми процессами массиву расстояний. Алгоритм позволяет ослабить требования к синхронизации данных этого массива между процессами (когда один процесс может не сразу увидеть новое значение расстояния, записанное другим процессом), за счёт, может быть, большего количества глобальных итераций.&lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Беллмана-Форда согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости вширь реализации алгоритма Беллмана-Форда.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера.&lt;br /&gt;
&lt;br /&gt;
[[file:Сильная_масштабируемость_вширь_алгоритма_Беллмана-Форда.png|thumb|center|700px|Рисунок 7. Параллельная реализация алгоритма Беллмана-Форда масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация алгоритма Беллмана-Форда, реализованная для CPU.  Все результаты получены на суперкомпьютере «Ломоносов-2». Использовались процессоры Intel Xeon E5-2697v3, задача решалась для графа большого размера на одном узле. &lt;br /&gt;
На рисунках показана эффективность реализации алгоритма Беллмана-Форда, запуск проводился на 1 узле для графа 2^27, выполнялась 1 итерация.&lt;br /&gt;
[[file:Bellman-Ford cpu.png|thumb|center|700px|Рисунок 8. График загрузки CPU при выполнении алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы не загружены и средний уровень загрузки составляет около 5%. Это достаточно неэффективная картина даже для программ, запущенных c использованием одного ядра в реализации. &lt;br /&gt;
&lt;br /&gt;
[[file:Bellman-Ford LoadAvg.png|thumb|center|700px|Рисунок 9. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно на уровне 1. Это указывает на постоянную загрузку аппаратных ресурсов не более чем 1 процессом, однако это число для узла слишком мало, что может указывать на не очень рациональное использование ресурсов.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford L1.png|thumb|center|700px|Рисунок 10. График кэш-промахов L1 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов очень высокое и находится на уровне 140 млн/сек. Это очень высокий показатель, показывающий потенциальную причину неэффективности.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford L2.png|thumb|center|700px|Рисунок 11. График кэш-промахов L2 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов второго уровня видно, что число таких промахов тоже крайне высокое и находится на уровне 140 млн/сек, что указывает на крайне неэффективную работу с памятью.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford-L3.png|thumb|center|700px|Рисунок 12. График кэш-промахов L3 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов последнего уровня видно, что число промахов тоже достаточно большое и составляет около 30 млн/сек по всем узлам. Это указывает на то, что задача очень плохо укладывается в кэш-память, и программа постоянно работает с оперативной памятью, что объясняется очень большим размером использованного графа.&lt;br /&gt;
&lt;br /&gt;
[[file:Bellman-Ford Inf Data.png|thumb|center|700px|Рисунок 13. График скорости передачи по сети Infiniband в байт/сек при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно высокая интенсивность использования сети на первом этапе. Это объясняется программной логикой, которая предполагает чтение графа из файла с диска, коммуникации с которым происходят на Ломоносов-2 через выделенную для этих задач сеть Infiniband.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford Inf Pckts.png|thumb|center|700px|Рисунок 14. График скорости передачи по сети Infiniband в пакетах/сек при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается аналогичная картина очень высокой интенсивности на первом этапе выполнения задачи. Далее сеть почти не используется.&lt;br /&gt;
 &lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала стабильно интенсивно,  однако очень неэффективно использовала память из-за очень большого размера графа.&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* OpenMP Stinger {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/10}}&lt;br /&gt;
* Nvidia nvGraph {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/19}}  &lt;br /&gt;
* Graph500 MPI {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/24}}&lt;br /&gt;
* Ligra {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/41}}&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En:Bellman-Ford algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%BE%D1%82_%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B_(SSSP)&amp;diff=25636</id>
		<title>Поиск кратчайшего пути от одной вершины (SSSP)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%BE%D1%82_%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B_(SSSP)&amp;diff=25636"/>
		<updated>2018-06-15T14:31:25Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-p}}&lt;br /&gt;
== Постановка задачи ==&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Последовательность &amp;lt;math&amp;gt;P(u, v)&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_1 = (u, w_1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e_2 = (w_1, w_2)&amp;lt;/math&amp;gt;, …, &amp;lt;math&amp;gt;e_k = (w_{k-1}, v)&amp;lt;/math&amp;gt; называется &#039;&#039;путём&#039;&#039;, идущим от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к вершине &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Суммарный вес этого пути равен&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f(P(u, v)) = \sum_{i = 1}^k f(e_i).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Требуется для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, достижимой из вершины-источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, указать путь &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt;, имеющий наименьший возможный суммарный вес:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f^*(v) = f(P^*(u, v)) = \min f(P(u, v)).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Название задачи на английском языке – «Single Source Shortest Path» (SSSP).&lt;br /&gt;
&lt;br /&gt;
== Варианты задачи ==&lt;br /&gt;
&lt;br /&gt;
Если требуется найти кратчайшие пути не от одной, а от всех вершин - см. [[Поиск кратчайшего пути для всех пар вершин (APSP)|поиск кратчайшего пути для всех пар вершин]].&lt;br /&gt;
&lt;br /&gt;
Задача может быть поставлена как для ориентированного, так и для неориентированного графа. Приведённая постановка задачи применима в обоих случаях.&lt;br /&gt;
&lt;br /&gt;
В зависимости от ограничений на возможные значения весов различают следующие случаи.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Единичные веса&#039;&#039;&#039;. В этом случае задача существенно упрощается и может быть решена за линейное время [[Поиск в ширину (BFS)|алгоритмом поиска вширь]].&lt;br /&gt;
* &#039;&#039;&#039;Положительные целые веса&#039;&#039;&#039;. Задача также может быть решена за линейное время&amp;lt;ref name=thorup&amp;gt;Thorup, Mikkel. “Undirected Single-Source Shortest Paths with Positive Integer Weights in Linear Time.” Journal of the ACM 46, no. 3 (May 1, 1999): 362–94. doi:10.1145/316542.316548.&amp;lt;/ref&amp;gt; (хотя и с большей константой).&lt;br /&gt;
* &#039;&#039;&#039;Неотрицательные веса&#039;&#039;&#039;. Часто встречающееся на практике ограничение, когда вес ребра соответствует времени или стоимости прохождения участка маршрута и не может быть отрицательным.&lt;br /&gt;
* &#039;&#039;&#039;Произвольные веса&#039;&#039;&#039;. Наиболее общая постановка. Граф не должен содержать циклов с отрицательным суммарным весом, иначе кратчайшего пути не существует.&lt;br /&gt;
&lt;br /&gt;
== Свойства задачи ==&lt;br /&gt;
&lt;br /&gt;
Решение задачи удовлетворяет принципу оптимальности: если путь &amp;lt;math&amp;gt;P^*(u, w)&amp;lt;/math&amp;gt; является частью кратчайшего пути &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;P^*(u, w)&amp;lt;/math&amp;gt; является кратчайшим путём от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Принцип оптимальности означает, что для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; вместо всего кратчайшего пути &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt; достаточно хранить его последнее ребро &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt;. Отсюда следует оценка объёма памяти &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Описание входных и выходных данных ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Преобразование выходных данных и поиск кратчайшего пути ===&lt;br /&gt;
&lt;br /&gt;
Кратчайший путь от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; может быть восстановлен за время &amp;lt;math&amp;gt;O(\left |P^*(u, v) \right |)&amp;lt;/math&amp;gt;, если, начиная с вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, проходить рёбра &amp;lt;math&amp;gt;e^*_w&amp;lt;/math&amp;gt; в обратном направлении до тех пор, пока не будет посещена вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рёбра &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt; могут быть восстановлены за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; перебором рёбер для каждой вершины:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
    e^*_v \in \{ (w, v) \in E \mid f^*(v) = f^*(w) + f((v, w)) \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Перебор может осуществляться параллельно.&lt;br /&gt;
&lt;br /&gt;
Для поиска кратчайших расстояний &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; по набору рёбер &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt;, необходимо построить из них дерево, на котором выполнить [[Поиск в ширину (BFS)|поиск в ширину]] за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (с возможностью параллелизации).&lt;br /&gt;
&lt;br /&gt;
== Алгоритмы решения задачи ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[[Поиск в ширину (BFS)]]&#039;&#039;&#039; для ориентированных невзвешенных графов, сложность &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Дейкстры]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1, 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/5}}&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Беллмана-Форда]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; для ориентированных графов с произвольными весами, сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;. {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/6}}&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Δ-шагания]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Meyer, U, and P Sanders. “Δ-Stepping: a Parallelizable Shortest Path Algorithm.” Journal of Algorithms 49, no. 1 (October 2003): 114–52. doi:10.1016/S0196-6774(03)00076-2.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, средняя сложность на графах со случайными весами &amp;lt;math&amp;gt;O(n + m + dL)&amp;lt;/math&amp;gt;, параллельная сложность &amp;lt;math&amp;gt;O((dL + \ln n) \ln n&amp;lt;/math&amp;gt; со средней работой &amp;lt;math&amp;gt;O(n + m + dL \ln n)&amp;lt;/math&amp;gt;. {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/2}}&lt;br /&gt;
* &#039;&#039;&#039;Алгоритм Торупа&#039;&#039;&#039;&amp;lt;ref name=thorup /&amp;gt; представляет собой теоретическое доказательство возможности решения задачи для неориентированных графов с положительными целыми весами за линейное время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В ориентированном ациклическом графе с произвольными весами время поиска кратчайших путей линейное – &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;, алгоритм основан на топологической сортировке графа.&lt;br /&gt;
&lt;br /&gt;
Обозначения: &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; – число рёбер, &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; – число вершин, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; – максимальная степень вершины, &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; – максимальный суммарный вес кратчайшего пути.&lt;br /&gt;
&lt;br /&gt;
Перечисленные алгоритмы могут использоваться и для решения [[Поиск кратчайшего пути для всех пар вершин (APSP)|задачи поиска кратчайшего пути для всех пар вершин графа]] (для этого необходимо запустить соответствующий алгоритм для каждой вершины графа, что при необходимости можно сделать параллельно), при этом оценка сложности умножается на &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En: Single Source Shortest Path (SSSP)]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=25635</id>
		<title>Алгоритм Дейкстры</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=25635"/>
		<updated>2018-06-15T14:30:00Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-a}}&lt;br /&gt;
Основные авторы описания: [[Участник:Daryin|А.Н.Дарьин]], [[Участник:VadimVV|Вад.В.Воеводин]] ([[#Описание локальности данных и вычислений|раздел 2.2]])&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Дейкстры&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа.&lt;br /&gt;
Алгоритм Дейкстры (с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt;) выполняется за время &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt; и является асимптотически быстрейшим из известных последовательных алгоритмов для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть уже вычислены все расстояния, не превосходящие некоторого числа &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;, то есть расстояния до вершин из множества &amp;lt;math&amp;gt;V_r = \{ v \in V \mid d(v) \le r \}&amp;lt;/math&amp;gt;. Пусть&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        (v, w) \in \arg\min \{ d(v) + f(e) \mid  v \in V, e = (v, w) \in E \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Тогда &amp;lt;math&amp;gt;d(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; лежит на кратчайшем пути от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Величины &amp;lt;math&amp;gt;d^+(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v \in V_r&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e = (v, w) \in E&amp;lt;/math&amp;gt;, называются &#039;&#039;предполагаемыми расстояниями&#039;&#039; и являются оценкой сверху для настоящих расстояний: &amp;lt;math&amp;gt;d(w) \le d^+(w)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры на каждом шаге находит вершину с наименьшим предполагаемым расстоянием, помечает её как посещённую и обновляет предполагаемые расстояния для всех концов рёбер, исходящих из неё.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основные вычисления связаны с операциями над очередью с приоритетом:&lt;br /&gt;
* извлечение минимального элемента (&amp;lt;code&amp;gt;delete_min&amp;lt;/code&amp;gt;);&lt;br /&gt;
* уменьшение приоритета элемента (&amp;lt;code&amp;gt;decrease_key&amp;lt;/code&amp;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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;Q&#039;&#039; := &#039;&#039;&#039;new&#039;&#039;&#039; priority queue&lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;:&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;v&#039;&#039; = &#039;&#039;u&#039;&#039; &#039;&#039;&#039;then&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := 0 &#039;&#039;&#039;else&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞ &lt;br /&gt;
     &#039;&#039;Q&#039;&#039;.insert(&#039;&#039;v&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;))&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;while&#039;&#039;&#039; &#039;&#039;Q&#039;&#039; ≠ ∅:&lt;br /&gt;
     &#039;&#039;v&#039;&#039; := &#039;&#039;Q&#039;&#039;.delete_min()&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
             &#039;&#039;Q&#039;&#039;.decrease_key(&#039;&#039;w&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;))&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Конкретная реализация алгоритма Дейкстры определяется выбором используемого алгоритма очереди с приоритетом. В простейшем случае это может быть массив или список, поиск минимума в котором требует просмотра всех вершин. Более эффективным является использование кучи; наилучшую известную оценку сложности имеет вариант с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Возможен вариант реализации, когда вершины добавляются в очередь не на этапе инициализации, а в момент первого посещения.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательная сложность алгоритма равна &amp;lt;math&amp;gt;O(C_1 m + C_2n)&amp;lt;/math&amp;gt;, где&lt;br /&gt;
* &amp;lt;math&amp;gt;C_1&amp;lt;/math&amp;gt; – количество операций уменьшения расстояния до вершины;&lt;br /&gt;
* &amp;lt;math&amp;gt;C_2&amp;lt;/math&amp;gt; – количество операций вычисления минимума.&lt;br /&gt;
&lt;br /&gt;
Оригинальный алгоритм Дейкстры использовал в качестве внутренней структуры данных списки, для которых &amp;lt;math&amp;gt;C_1 = O(1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;C_2 = O(n)&amp;lt;/math&amp;gt;, так что общая сложность составляла &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При использовании фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt; время вычисления минимума сокращается до &amp;lt;math&amp;gt;C_2 = O(\ln n)&amp;lt;/math&amp;gt;, так что общая сложность равна &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;, что является асимптотически наилучшим известным результатом для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
Приводится граф алгоритма для базовой реализации алгоритма Дейкстры на списках или массивах.&lt;br /&gt;
[[file:Deikstra.png|thumb|center|600px|Рисунок 1. Граф алгоритма без отображения входных и выходных данных. n=3. Желтым цветом обозначены операции сравнения, зеленым - операции изменения меток вершин, синим - пометка вершины.]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры допускает эффективную параллелизацию&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;, среднее время работы &amp;lt;math&amp;gt;O(n^{1/3}\ln n)&amp;lt;/math&amp;gt; с объёмом вычислений &amp;lt;math&amp;gt;O(n \ln n + m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Дейкстры.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(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;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_1.png|thumb|center|700px|Рисунок 1. Реализация алгоритма Дейкстры. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 1 представлен профиль обращений в память для реализации алгоритма Дейкстры. Первое, что бросается в глаза, – большая разрозненность обращений. В частности, значительные области выше и ниже фрагмента 2 остаются пустыми, при этом сами обращения объединены лишь в небольшие группы. Это говорит о низкой эффективности, поскольку: а) повторные обращения практически отсутствуют либо происходят через значительный промежуток времени; б) расстояния между идущими подряд обращениями может быть очень большим.&lt;br /&gt;
&lt;br /&gt;
Однако при ближайшем рассмотрении может оказаться, что такие участки обладают высокой локальностью и состоят из значительного числа обращений. Более того, на общем профиле есть несколько областей (фрагменты 1 и 2), в которых обращения хорошо локализованы. Необходимо исследовать отдельные участки более подробно.&lt;br /&gt;
&lt;br /&gt;
Перейдем к изучению фрагмента 1 (рис. 2), в рамках которого выполняются обращения к двум небольшим массивам. Можно увидеть, что здесь задействованы только примерно 500 элементов, при этом к ним выполняется около 100 тысяч обращений. Весь профиль составляет около 120 тысяч обращений, поэтому получается, что подавляющая их часть выполняется именно к этим элементам. &lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_2.png|thumb|center|700px|Рисунок 2. Профиль обращений, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Поскольку число элементов невелико, локальность в данном случае заведомо будет достаточно высокой, независимо от структуры самого фрагмента.  Однако на рис. 3, где представлены два участка из фрагмента 1, можно увидеть, что фрагмент в основном состоит из последовательных переборов, при этом данные зачастую задействованы повторно через не очень большие промежутки. Все это позволяет говорить, что данный фрагмент обладает высокой как пространственной, так и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_3.png|thumb|center|700px|Рисунок 3. Профили двух участков фрагмента 1 (выделены зеленым на рис. 2)]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим теперь более подробно фрагмент 2 (рис. 4), в рамках которого выполняются обращения к еще служебному массиву. Здесь профиль состоит из двух этапов. На первом заметен достаточно хаотичный разброс обращений, напоминающий случайный доступ. На втором обращения образуют подобие последовательного перебора.  В целом такой профиль характеризуется очень низкой временной локальностью (повторные обращения практически или полностью отсутствуют) и достаточно низкой пространственной локальностью (из-за случайного доступа на первом этапе).&lt;br /&gt;
&lt;br /&gt;
Заметим, что при этом число задействованных элементов здесь больше, чем во фрагменте 1, однако число обращений гораздо меньше.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_4.png|thumb|center|500px|Рисунок 4. Профиль обращений, фрагмент 2]]&lt;br /&gt;
&lt;br /&gt;
Далее остаются для рассмотрения два массива (область между фрагментами 1 и 2 и область ниже фрагмента 2). Характер обращений к этим массивам во многом похож, поэтому достаточно изучить более подробно только один из них. &lt;br /&gt;
&lt;br /&gt;
Фрагмент 3 рассмотрен на рис. 5. Этот участок отражаeт достаточно большую область, что не позволяет проанализировать профиль вплоть до отдельных обращений, однако здесь этого и не требуется. Из данного фрагмента видно, что основу профиля составляют участки с последовательным (или с небольшим шагом) перебором, состоящие из небольшого числа элементов – выделенный желтым самый большой участок фрагмента состоит всего из пары сотен обращений. При этом между разными участками расстояние может быть существенным. Все это говорит об очень низкой локальности (как пространственной, так и временной) в случае двух рассматриваемых массивов.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_5.png|thumb|center|500px|Рисунок 5. Профиль обращений, фрагмент 3]]&lt;br /&gt;
&lt;br /&gt;
В целом, несмотря на положительный вклад массивов из фрагмента 1, локальность общего профиля должна быть достаточно низкой, вследствие неэффективного использования данных в остальной части профиля.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Основной фрагмент реализации, на основе которого были получены количественные оценки, приведен [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/benchmarks/dijkstra/dijkstra.h здесь] (функция Kernel). Условия запуска описаны [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/README.md здесь].&lt;br /&gt;
&lt;br /&gt;
Первая оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью достаточно низка. Это неудивительно, поскольку реализации алгоритмов над графами почти всегда обладают низкой эффективностью вследствие нерегулярности доступа к данным, что мы и увидели при анализе профиля обращений.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
Вторая характеристика – cvg – предназначена для получения более машинно-независимой оценки локальности. Она определяет, насколько часто в программе необходимо подтягивать данные в кэш-память. Соответственно, чем меньше значение cvg, тем реже это нужно делать, тем лучше локальность. &lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения cvg для того же набора реализаций, отсортированные по убыванию (чем меньше cvg, тем в общем случае выше локальность). Можно увидеть, что в данном случае значение cvg хорошо коррелирует с оценкой производительности и отражает низкую локальность, что соответствует выводам, сделанным при качественной оценке локальности.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_cvg.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки cvg]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot;&amp;lt;ref name=&amp;quot;Lom&amp;quot;&amp;gt;Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&amp;lt;/ref&amp;gt; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [4, 8 : 128] со значениями квадрата целого числа;&lt;br /&gt;
*	размер графа [16000:64000] с шагом 16000.&lt;br /&gt;
&lt;br /&gt;
На следующем рисунке приведен график [[Глоссарий#Производительность|производительности]] выбранной реализации алгоритма в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra perf.png|thumb|center|700px|Рисунок 8. Параллельная реализация алгоритма. Изменение производительности в зависимости от числа процессоров и размера области.]]&lt;br /&gt;
&lt;br /&gt;
В силу особенностей параллельной реализации алгоритма производительность в целом достаточно низкая и с ростом числа процессов увеличивается медленно, а при приближении к числу процессов 128 начинает уменьшаться.&lt;br /&gt;
Это объясняется использованием коллективных операций на каждой итерации алгоритма и тем, что затраты на коммуникационные обмены существенно возрастают с ростом числа использованных процессов. Вычисления на каждом процессе проходят достаточно быстро и потому декомпозиция графа слабо компенсирует эффект от затрат на коммуникационные обмены. &lt;br /&gt;
&lt;br /&gt;
[https://code.google.com/p/prir-dijkstra-mpi/source/browse/trunk/project/dijkstra_mpi.c Исследованная параллельная реализация на языке C]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация алгоритма Дейкстры.  Все результаты получены на суперкомпьютере &amp;quot;Ломоносов&amp;quot;. Использовались процессоры Intel Xeon X5570 с пиковой производительностью в 94 Гфлопс, а также компилятор intel 13.1.0. &lt;br /&gt;
На рисунках показана эффективность реализации алгоритма Дейкстры  на 32 процессах.&lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra CPU User.png|thumb|center|700px|Рисунок 9. График загрузки CPU при выполнении алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы уровень загрузки составляет около 50%. Это указывает на равномерную загруженность вычислениями процессоров, при использовании 8 процессов на вычислительный узел и без использования Hyper Threading. &lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra Flops.png|thumb|center|700px|Рисунок 10. График операций с плавающей точкой в секунду при выполнении алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На Рисунке 10 показан график количества операций с плавающей точкой в секунду. На графике видна общая очень низкая производительность вычислений около 250 Kфлопс в пике и около 150 Кфлопс в среднем по всем узлам. Это указывает то, что в программе почти все вычисления производятся с целыми числами. &lt;br /&gt;
 [[file:Dijkstra L1.png|thumb|center|700px|Рисунок 11. График кэш-промахов L1 в секунду при работе алгоритма Дейкстры]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов очень большое для нескольких ядер и находится на уровне 15 млн/сек (в пике до 60 млн/сек), что указывает на интенсивные вычисления в части процессов. В среднем по всем узлам значения значительно ниже (около 9 млн/сек). Это указывает на неравномерное распределение вычислений.&lt;br /&gt;
 [[file:Dijkstra L3.png|thumb|center|700px|Рисунок 12. График кэш-промахов L3 в секунду при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов третьего уровня видно, что число промахов очень невелико и находится на уровне 1,2 млн/сек, однако в среднем по всем узлам значения около 0,5 млн/сек. Соотношение кэш промахов L1|L3 для процессов с высокой производительностью доходит до 60, однако в среднем около 30. Это указывает на очень хорошую локальность вычислений как у части процессов, так и для всех в среднем, и это является признаком высокой производительности. &lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra Mem Load.png|thumb|center|700px|Рисунок 13. График количества чтений из оперативной памяти при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике обращений в память видна достаточно типичная картина для таких приложений. Активность чтения достаточно низкая, что в совокупности с низкими значениями кэш-промахов L3 указывает на хорошую локальность. Хорошая локальность приложения также указывает на то, что значение около 1 млн/сек для задачи является результатом высокой производительности вычислений, хотя и присутствует неравномерность между процессами.&lt;br /&gt;
 [[file:Dijkstra Mem Store.png|thumb|center|700px|Рисунок 14. График количества записей в оперативную память при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике записей в память видна похожая картина неравномерности вычислений, при которой одновременно активно выполняют запись только несколько процессов. Это коррелирует с другими графиками выполнения. Стоит отметить достаточно низкое число обращений на запись в память. Это указывает на хорошую организацию вычислений и достаточно эффективную работу с памятью.&lt;br /&gt;
 [[file:Dijkstra Inf DATA.png|thumb|center|700px|Рисунок 15. График скорости передачи по сети Infiniband в байт/сек при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно высокая скорость передачи данных в байтах в секунду. Это говорит о том, что процессы между собой обмениваются интенсивно и вероятно достаточно малыми порциями данных, потому как производительность вычислений высока. Стоит отметить, что скорость передачи отличается между процессами, что указывает на дисбаланс вычислений.&lt;br /&gt;
 [[file:Dijkstra Inf PCKTS.png|thumb|center|700px|Рисунок 16. График скорости передачи по сети Infiniband в пакетах/сек при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается крайне высокая интенсивность передачи данных. Это говорит о том, что, вероятно, процессы обмениваются не очень существенными объемами данных, но очень интенсивно. Используются коллективные операции на каждом шаге с небольшими порциями данных, что объясняет такую картину.  Также наблюдается меньший дизбаланс между процессами, чем наблюдаемый в графиках использования памяти, вычислений и передачи данных в байтах/сек. Это указывает на то, что процессы обмениваются по алгоритму одинаковым числом пакетов, однако получают разные объемы данных и ведут неравномерные вычисления. &lt;br /&gt;
 [[file:Dijkstra Load ABG.png|thumb|center|700px|Рисунок 17. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе алгоритма Дейкстры]]&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно и приблизительно равняется 8. Это свидетельствует о стабильной работе программы с загруженными вычислениями всеми узлами. Это указывает на очень рациональную и статичную загрузку аппаратных ресурсов процессами. И показывает достаточно хорошую эффективность выполняемой реализации.&lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала достаточно эффективно, и стабильно. Использование памяти  очень интенсивное, а использование коммуникационной среды крайне интенсивное, при этом объемы передаваемых данных не являются высокими. Это указывает на требовательность к латентности коммуникационной среды алгоритмической части программы. &lt;br /&gt;
Низкая эффективность связана, судя по всему, с достаточно высоким объемом пересылок на каждом процессе и с интенсивными обменами сообщениями.&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths.html dijkstra_shortest_paths]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths_no_color_map.html dijkstra_shortest_paths_no_color_map]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. &lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]: {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/14}}&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#eager-dijkstra-s-algorithm eager_dijkstra_shortest_paths]&amp;lt;/code&amp;gt; – непосредственная реализация алгоритма Дейкстры;&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#crauser-et-al-s-algorithm crauser_et_al_shortest_paths]&amp;lt;/code&amp;gt; – реализация алгоритма Дейкстры в виде алгоритма из статьи Краузера и др.&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt; &lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.single_source_dijkstra.html single_source_dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python/C++: [https://networkit.iti.kit.edu NetworKit] (класс &amp;lt;code&amp;gt;[https://networkit.iti.kit.edu/data/uploads/docs/NetworKit-Doc/python/html/graph.html#networkit.graph.Dijkstra networkit.graph.Dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En:Dijkstra&#039;s algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%CE%94-%D1%88%D0%B0%D0%B3%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=25634</id>
		<title>Алгоритм Δ-шагания</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%CE%94-%D1%88%D0%B0%D0%B3%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=25634"/>
		<updated>2018-06-15T10:41:41Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-a}}&lt;br /&gt;
&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/2}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм дельта-шагания&#039;&#039;&#039;&amp;lt;ref&amp;gt;Meyer, U, and P Sanders. “Δ-Stepping: a Parallelizable Shortest Path Algorithm.” Journal of Algorithms 49, no. 1 (October 2003): 114–52. doi:10.1016/S0196-6774(03)00076-2.&amp;lt;/ref&amp;gt; (англ. Δ-Stepping) предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм изначально проектировался с целью эффективной параллелизации.&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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;W&#039;&#039;;&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;;&lt;br /&gt;
   параметр &#039;&#039;Δ&#039;&#039; &amp;gt; 0.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; DeltaStepping(&#039;&#039;V&#039;&#039;, &#039;&#039;E&#039;&#039;, &#039;&#039;W&#039;&#039;, &#039;&#039;u&#039;&#039;, &#039;&#039;Δ&#039;&#039;):&lt;br /&gt;
      &#039;&#039;LightEdges&#039;&#039; := { e ∈ E | W(e) ≤ Δ }&lt;br /&gt;
      &#039;&#039;HeavyEdges&#039;&#039; := { e ∈ E | W(e) &amp;gt; Δ }&lt;br /&gt;
      &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
      Relax(&#039;&#039;u&#039;&#039;, 0)&lt;br /&gt;
      &#039;&#039;&#039;while&#039;&#039;&#039; any({ &#039;&#039;Buckets&#039;&#039;(i) ≠ ∅ }):&lt;br /&gt;
          &#039;&#039;Bucket&#039;&#039; := first({ &#039;&#039;Buckets&#039;&#039;(i) ≠ ∅ })&lt;br /&gt;
          &#039;&#039;Deleted&#039;&#039; := ∅&lt;br /&gt;
          &#039;&#039;&#039;while&#039;&#039;&#039; &#039;&#039;Bucket&#039;&#039; ≠ ∅:&lt;br /&gt;
              &#039;&#039;Requests&#039;&#039; := FindRequests(&#039;&#039;Bucket&#039;&#039;, &#039;&#039;LightEdges&#039;&#039;)&lt;br /&gt;
              &#039;&#039;Deleted&#039;&#039; := &#039;&#039;Deleted&#039;&#039; ∪ &#039;&#039;Bucket&#039;&#039;&lt;br /&gt;
              &#039;&#039;Bucket&#039;&#039; := ∅&lt;br /&gt;
              RelaxAll(&#039;&#039;Requests&#039;&#039;)&lt;br /&gt;
          RelaxAll(FindRequests(&#039;&#039;Deleted&#039;&#039;, &#039;&#039;HeavyEdges&#039;&#039;))&lt;br /&gt;
          &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; Relax(&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;x&#039;&#039; &amp;lt; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;):&lt;br /&gt;
         &#039;&#039;OldBucket&#039;&#039; := B(⌊&#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) / &#039;&#039;Δ&#039;&#039;⌋)&lt;br /&gt;
         &#039;&#039;NewBucket&#039;&#039; := B(⌊&#039;&#039;x&#039;&#039; / &#039;&#039;Δ&#039;&#039;⌋)&lt;br /&gt;
         &#039;&#039;OldBucket&#039;&#039; := &#039;&#039;OldBucket&#039;&#039; \ {&#039;&#039;v&#039;&#039;}&lt;br /&gt;
         &#039;&#039;NewBucket&#039;&#039; := &#039;&#039;NewBucket&#039;&#039; ∪ {&#039;&#039;v&#039;&#039;}&lt;br /&gt;
         &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;x&#039;&#039;&lt;br /&gt;
         &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; RelaxAll(&#039;&#039;R&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; (&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;) ∈ &#039;&#039;R&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; Relax(&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;)&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; FindRequests(&#039;&#039;V&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;, &#039;&#039;E&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;return&#039;&#039;&#039; { (&#039;&#039;w&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;W&#039;&#039;(&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;)) | &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039; and (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;}&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Средняя последовательная сложность алгоритма на графах со случайными весами &amp;lt;math&amp;gt;O(n + m + dL)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; – максимальный суммарный вес кратчайшего пути, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; – максимальная длина кратчайшего пути.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Средняя время работы параллельного алгоритма на графах со случайными весами на &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; процессорах составляет &amp;lt;math&amp;gt;O((dL + \ln n) \ln n)&amp;lt;/math&amp;gt; со средней работой &amp;lt;math&amp;gt;O(n + m + dL \ln n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Предельные случаи алгоритма Δ-шагания:&lt;br /&gt;
* при &amp;lt;math&amp;gt;\Delta \to 0&amp;lt;/math&amp;gt;: [[алгоритм Дейкстры]];&lt;br /&gt;
* при &amp;lt;math&amp;gt;\Delta \to \infty&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;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#delta-stepping-algorithm delta_stepping_shortest_paths]&amp;lt;/code&amp;gt;). {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/25}}&lt;br /&gt;
* Gap: {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/38}}&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;br /&gt;
&lt;br /&gt;
[[en:Δ-stepping algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=25633</id>
		<title>Алгоритм Дейкстры</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=25633"/>
		<updated>2018-06-15T09:52:02Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-a}}&lt;br /&gt;
&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/5}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
Основные авторы описания: [[Участник:Daryin|А.Н.Дарьин]], [[Участник:VadimVV|Вад.В.Воеводин]] ([[#Описание локальности данных и вычислений|раздел 2.2]])&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Дейкстры&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа.&lt;br /&gt;
Алгоритм Дейкстры (с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt;) выполняется за время &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt; и является асимптотически быстрейшим из известных последовательных алгоритмов для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть уже вычислены все расстояния, не превосходящие некоторого числа &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;, то есть расстояния до вершин из множества &amp;lt;math&amp;gt;V_r = \{ v \in V \mid d(v) \le r \}&amp;lt;/math&amp;gt;. Пусть&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        (v, w) \in \arg\min \{ d(v) + f(e) \mid  v \in V, e = (v, w) \in E \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Тогда &amp;lt;math&amp;gt;d(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; лежит на кратчайшем пути от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Величины &amp;lt;math&amp;gt;d^+(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v \in V_r&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e = (v, w) \in E&amp;lt;/math&amp;gt;, называются &#039;&#039;предполагаемыми расстояниями&#039;&#039; и являются оценкой сверху для настоящих расстояний: &amp;lt;math&amp;gt;d(w) \le d^+(w)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры на каждом шаге находит вершину с наименьшим предполагаемым расстоянием, помечает её как посещённую и обновляет предполагаемые расстояния для всех концов рёбер, исходящих из неё.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основные вычисления связаны с операциями над очередью с приоритетом:&lt;br /&gt;
* извлечение минимального элемента (&amp;lt;code&amp;gt;delete_min&amp;lt;/code&amp;gt;);&lt;br /&gt;
* уменьшение приоритета элемента (&amp;lt;code&amp;gt;decrease_key&amp;lt;/code&amp;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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;Q&#039;&#039; := &#039;&#039;&#039;new&#039;&#039;&#039; priority queue&lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;:&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;v&#039;&#039; = &#039;&#039;u&#039;&#039; &#039;&#039;&#039;then&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := 0 &#039;&#039;&#039;else&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞ &lt;br /&gt;
     &#039;&#039;Q&#039;&#039;.insert(&#039;&#039;v&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;))&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;while&#039;&#039;&#039; &#039;&#039;Q&#039;&#039; ≠ ∅:&lt;br /&gt;
     &#039;&#039;v&#039;&#039; := &#039;&#039;Q&#039;&#039;.delete_min()&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
             &#039;&#039;Q&#039;&#039;.decrease_key(&#039;&#039;w&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;))&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Конкретная реализация алгоритма Дейкстры определяется выбором используемого алгоритма очереди с приоритетом. В простейшем случае это может быть массив или список, поиск минимума в котором требует просмотра всех вершин. Более эффективным является использование кучи; наилучшую известную оценку сложности имеет вариант с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Возможен вариант реализации, когда вершины добавляются в очередь не на этапе инициализации, а в момент первого посещения.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательная сложность алгоритма равна &amp;lt;math&amp;gt;O(C_1 m + C_2n)&amp;lt;/math&amp;gt;, где&lt;br /&gt;
* &amp;lt;math&amp;gt;C_1&amp;lt;/math&amp;gt; – количество операций уменьшения расстояния до вершины;&lt;br /&gt;
* &amp;lt;math&amp;gt;C_2&amp;lt;/math&amp;gt; – количество операций вычисления минимума.&lt;br /&gt;
&lt;br /&gt;
Оригинальный алгоритм Дейкстры использовал в качестве внутренней структуры данных списки, для которых &amp;lt;math&amp;gt;C_1 = O(1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;C_2 = O(n)&amp;lt;/math&amp;gt;, так что общая сложность составляла &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При использовании фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt; время вычисления минимума сокращается до &amp;lt;math&amp;gt;C_2 = O(\ln n)&amp;lt;/math&amp;gt;, так что общая сложность равна &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;, что является асимптотически наилучшим известным результатом для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
Приводится граф алгоритма для базовой реализации алгоритма Дейкстры на списках или массивах.&lt;br /&gt;
[[file:Deikstra.png|thumb|center|600px|Рисунок 1. Граф алгоритма без отображения входных и выходных данных. n=3. Желтым цветом обозначены операции сравнения, зеленым - операции изменения меток вершин, синим - пометка вершины.]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры допускает эффективную параллелизацию&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;, среднее время работы &amp;lt;math&amp;gt;O(n^{1/3}\ln n)&amp;lt;/math&amp;gt; с объёмом вычислений &amp;lt;math&amp;gt;O(n \ln n + m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Дейкстры.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(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;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_1.png|thumb|center|700px|Рисунок 1. Реализация алгоритма Дейкстры. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 1 представлен профиль обращений в память для реализации алгоритма Дейкстры. Первое, что бросается в глаза, – большая разрозненность обращений. В частности, значительные области выше и ниже фрагмента 2 остаются пустыми, при этом сами обращения объединены лишь в небольшие группы. Это говорит о низкой эффективности, поскольку: а) повторные обращения практически отсутствуют либо происходят через значительный промежуток времени; б) расстояния между идущими подряд обращениями может быть очень большим.&lt;br /&gt;
&lt;br /&gt;
Однако при ближайшем рассмотрении может оказаться, что такие участки обладают высокой локальностью и состоят из значительного числа обращений. Более того, на общем профиле есть несколько областей (фрагменты 1 и 2), в которых обращения хорошо локализованы. Необходимо исследовать отдельные участки более подробно.&lt;br /&gt;
&lt;br /&gt;
Перейдем к изучению фрагмента 1 (рис. 2), в рамках которого выполняются обращения к двум небольшим массивам. Можно увидеть, что здесь задействованы только примерно 500 элементов, при этом к ним выполняется около 100 тысяч обращений. Весь профиль составляет около 120 тысяч обращений, поэтому получается, что подавляющая их часть выполняется именно к этим элементам. &lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_2.png|thumb|center|700px|Рисунок 2. Профиль обращений, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Поскольку число элементов невелико, локальность в данном случае заведомо будет достаточно высокой, независимо от структуры самого фрагмента.  Однако на рис. 3, где представлены два участка из фрагмента 1, можно увидеть, что фрагмент в основном состоит из последовательных переборов, при этом данные зачастую задействованы повторно через не очень большие промежутки. Все это позволяет говорить, что данный фрагмент обладает высокой как пространственной, так и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_3.png|thumb|center|700px|Рисунок 3. Профили двух участков фрагмента 1 (выделены зеленым на рис. 2)]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим теперь более подробно фрагмент 2 (рис. 4), в рамках которого выполняются обращения к еще служебному массиву. Здесь профиль состоит из двух этапов. На первом заметен достаточно хаотичный разброс обращений, напоминающий случайный доступ. На втором обращения образуют подобие последовательного перебора.  В целом такой профиль характеризуется очень низкой временной локальностью (повторные обращения практически или полностью отсутствуют) и достаточно низкой пространственной локальностью (из-за случайного доступа на первом этапе).&lt;br /&gt;
&lt;br /&gt;
Заметим, что при этом число задействованных элементов здесь больше, чем во фрагменте 1, однако число обращений гораздо меньше.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_4.png|thumb|center|500px|Рисунок 4. Профиль обращений, фрагмент 2]]&lt;br /&gt;
&lt;br /&gt;
Далее остаются для рассмотрения два массива (область между фрагментами 1 и 2 и область ниже фрагмента 2). Характер обращений к этим массивам во многом похож, поэтому достаточно изучить более подробно только один из них. &lt;br /&gt;
&lt;br /&gt;
Фрагмент 3 рассмотрен на рис. 5. Этот участок отражаeт достаточно большую область, что не позволяет проанализировать профиль вплоть до отдельных обращений, однако здесь этого и не требуется. Из данного фрагмента видно, что основу профиля составляют участки с последовательным (или с небольшим шагом) перебором, состоящие из небольшого числа элементов – выделенный желтым самый большой участок фрагмента состоит всего из пары сотен обращений. При этом между разными участками расстояние может быть существенным. Все это говорит об очень низкой локальности (как пространственной, так и временной) в случае двух рассматриваемых массивов.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_5.png|thumb|center|500px|Рисунок 5. Профиль обращений, фрагмент 3]]&lt;br /&gt;
&lt;br /&gt;
В целом, несмотря на положительный вклад массивов из фрагмента 1, локальность общего профиля должна быть достаточно низкой, вследствие неэффективного использования данных в остальной части профиля.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Основной фрагмент реализации, на основе которого были получены количественные оценки, приведен [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/benchmarks/dijkstra/dijkstra.h здесь] (функция Kernel). Условия запуска описаны [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/README.md здесь].&lt;br /&gt;
&lt;br /&gt;
Первая оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью достаточно низка. Это неудивительно, поскольку реализации алгоритмов над графами почти всегда обладают низкой эффективностью вследствие нерегулярности доступа к данным, что мы и увидели при анализе профиля обращений.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
Вторая характеристика – cvg – предназначена для получения более машинно-независимой оценки локальности. Она определяет, насколько часто в программе необходимо подтягивать данные в кэш-память. Соответственно, чем меньше значение cvg, тем реже это нужно делать, тем лучше локальность. &lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения cvg для того же набора реализаций, отсортированные по убыванию (чем меньше cvg, тем в общем случае выше локальность). Можно увидеть, что в данном случае значение cvg хорошо коррелирует с оценкой производительности и отражает низкую локальность, что соответствует выводам, сделанным при качественной оценке локальности.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_cvg.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки cvg]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot;&amp;lt;ref name=&amp;quot;Lom&amp;quot;&amp;gt;Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&amp;lt;/ref&amp;gt; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [4, 8 : 128] со значениями квадрата целого числа;&lt;br /&gt;
*	размер графа [16000:64000] с шагом 16000.&lt;br /&gt;
&lt;br /&gt;
На следующем рисунке приведен график [[Глоссарий#Производительность|производительности]] выбранной реализации алгоритма в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra perf.png|thumb|center|700px|Рисунок 8. Параллельная реализация алгоритма. Изменение производительности в зависимости от числа процессоров и размера области.]]&lt;br /&gt;
&lt;br /&gt;
В силу особенностей параллельной реализации алгоритма производительность в целом достаточно низкая и с ростом числа процессов увеличивается медленно, а при приближении к числу процессов 128 начинает уменьшаться.&lt;br /&gt;
Это объясняется использованием коллективных операций на каждой итерации алгоритма и тем, что затраты на коммуникационные обмены существенно возрастают с ростом числа использованных процессов. Вычисления на каждом процессе проходят достаточно быстро и потому декомпозиция графа слабо компенсирует эффект от затрат на коммуникационные обмены. &lt;br /&gt;
&lt;br /&gt;
[https://code.google.com/p/prir-dijkstra-mpi/source/browse/trunk/project/dijkstra_mpi.c Исследованная параллельная реализация на языке C]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация алгоритма Дейкстры.  Все результаты получены на суперкомпьютере &amp;quot;Ломоносов&amp;quot;. Использовались процессоры Intel Xeon X5570 с пиковой производительностью в 94 Гфлопс, а также компилятор intel 13.1.0. &lt;br /&gt;
На рисунках показана эффективность реализации алгоритма Дейкстры  на 32 процессах.&lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra CPU User.png|thumb|center|700px|Рисунок 9. График загрузки CPU при выполнении алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы уровень загрузки составляет около 50%. Это указывает на равномерную загруженность вычислениями процессоров, при использовании 8 процессов на вычислительный узел и без использования Hyper Threading. &lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra Flops.png|thumb|center|700px|Рисунок 10. График операций с плавающей точкой в секунду при выполнении алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На Рисунке 10 показан график количества операций с плавающей точкой в секунду. На графике видна общая очень низкая производительность вычислений около 250 Kфлопс в пике и около 150 Кфлопс в среднем по всем узлам. Это указывает то, что в программе почти все вычисления производятся с целыми числами. &lt;br /&gt;
 [[file:Dijkstra L1.png|thumb|center|700px|Рисунок 11. График кэш-промахов L1 в секунду при работе алгоритма Дейкстры]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов очень большое для нескольких ядер и находится на уровне 15 млн/сек (в пике до 60 млн/сек), что указывает на интенсивные вычисления в части процессов. В среднем по всем узлам значения значительно ниже (около 9 млн/сек). Это указывает на неравномерное распределение вычислений.&lt;br /&gt;
 [[file:Dijkstra L3.png|thumb|center|700px|Рисунок 12. График кэш-промахов L3 в секунду при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов третьего уровня видно, что число промахов очень невелико и находится на уровне 1,2 млн/сек, однако в среднем по всем узлам значения около 0,5 млн/сек. Соотношение кэш промахов L1|L3 для процессов с высокой производительностью доходит до 60, однако в среднем около 30. Это указывает на очень хорошую локальность вычислений как у части процессов, так и для всех в среднем, и это является признаком высокой производительности. &lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra Mem Load.png|thumb|center|700px|Рисунок 13. График количества чтений из оперативной памяти при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике обращений в память видна достаточно типичная картина для таких приложений. Активность чтения достаточно низкая, что в совокупности с низкими значениями кэш-промахов L3 указывает на хорошую локальность. Хорошая локальность приложения также указывает на то, что значение около 1 млн/сек для задачи является результатом высокой производительности вычислений, хотя и присутствует неравномерность между процессами.&lt;br /&gt;
 [[file:Dijkstra Mem Store.png|thumb|center|700px|Рисунок 14. График количества записей в оперативную память при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике записей в память видна похожая картина неравномерности вычислений, при которой одновременно активно выполняют запись только несколько процессов. Это коррелирует с другими графиками выполнения. Стоит отметить достаточно низкое число обращений на запись в память. Это указывает на хорошую организацию вычислений и достаточно эффективную работу с памятью.&lt;br /&gt;
 [[file:Dijkstra Inf DATA.png|thumb|center|700px|Рисунок 15. График скорости передачи по сети Infiniband в байт/сек при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно высокая скорость передачи данных в байтах в секунду. Это говорит о том, что процессы между собой обмениваются интенсивно и вероятно достаточно малыми порциями данных, потому как производительность вычислений высока. Стоит отметить, что скорость передачи отличается между процессами, что указывает на дисбаланс вычислений.&lt;br /&gt;
 [[file:Dijkstra Inf PCKTS.png|thumb|center|700px|Рисунок 16. График скорости передачи по сети Infiniband в пакетах/сек при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается крайне высокая интенсивность передачи данных. Это говорит о том, что, вероятно, процессы обмениваются не очень существенными объемами данных, но очень интенсивно. Используются коллективные операции на каждом шаге с небольшими порциями данных, что объясняет такую картину.  Также наблюдается меньший дизбаланс между процессами, чем наблюдаемый в графиках использования памяти, вычислений и передачи данных в байтах/сек. Это указывает на то, что процессы обмениваются по алгоритму одинаковым числом пакетов, однако получают разные объемы данных и ведут неравномерные вычисления. &lt;br /&gt;
 [[file:Dijkstra Load ABG.png|thumb|center|700px|Рисунок 17. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе алгоритма Дейкстры]]&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно и приблизительно равняется 8. Это свидетельствует о стабильной работе программы с загруженными вычислениями всеми узлами. Это указывает на очень рациональную и статичную загрузку аппаратных ресурсов процессами. И показывает достаточно хорошую эффективность выполняемой реализации.&lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала достаточно эффективно, и стабильно. Использование памяти  очень интенсивное, а использование коммуникационной среды крайне интенсивное, при этом объемы передаваемых данных не являются высокими. Это указывает на требовательность к латентности коммуникационной среды алгоритмической части программы. &lt;br /&gt;
Низкая эффективность связана, судя по всему, с достаточно высоким объемом пересылок на каждом процессе и с интенсивными обменами сообщениями.&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths.html dijkstra_shortest_paths]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths_no_color_map.html dijkstra_shortest_paths_no_color_map]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. &lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]: {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/14}}&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#eager-dijkstra-s-algorithm eager_dijkstra_shortest_paths]&amp;lt;/code&amp;gt; – непосредственная реализация алгоритма Дейкстры;&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#crauser-et-al-s-algorithm crauser_et_al_shortest_paths]&amp;lt;/code&amp;gt; – реализация алгоритма Дейкстры в виде алгоритма из статьи Краузера и др.&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt; &lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.single_source_dijkstra.html single_source_dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python/C++: [https://networkit.iti.kit.edu NetworKit] (класс &amp;lt;code&amp;gt;[https://networkit.iti.kit.edu/data/uploads/docs/NetworKit-Doc/python/html/graph.html#networkit.graph.Dijkstra networkit.graph.Dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En:Dijkstra&#039;s algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%CE%94-%D1%88%D0%B0%D0%B3%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=25632</id>
		<title>Алгоритм Δ-шагания</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%CE%94-%D1%88%D0%B0%D0%B3%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=25632"/>
		<updated>2018-06-15T09:49:55Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-a}}&lt;br /&gt;
&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/2}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм дельта-шагания&#039;&#039;&#039;&amp;lt;ref&amp;gt;Meyer, U, and P Sanders. “Δ-Stepping: a Parallelizable Shortest Path Algorithm.” Journal of Algorithms 49, no. 1 (October 2003): 114–52. doi:10.1016/S0196-6774(03)00076-2.&amp;lt;/ref&amp;gt; (англ. Δ-Stepping) предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм изначально проектировался с целью эффективной параллелизации.&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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;W&#039;&#039;;&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;;&lt;br /&gt;
   параметр &#039;&#039;Δ&#039;&#039; &amp;gt; 0.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; DeltaStepping(&#039;&#039;V&#039;&#039;, &#039;&#039;E&#039;&#039;, &#039;&#039;W&#039;&#039;, &#039;&#039;u&#039;&#039;, &#039;&#039;Δ&#039;&#039;):&lt;br /&gt;
      &#039;&#039;LightEdges&#039;&#039; := { e ∈ E | W(e) ≤ Δ }&lt;br /&gt;
      &#039;&#039;HeavyEdges&#039;&#039; := { e ∈ E | W(e) &amp;gt; Δ }&lt;br /&gt;
      &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
      Relax(&#039;&#039;u&#039;&#039;, 0)&lt;br /&gt;
      &#039;&#039;&#039;while&#039;&#039;&#039; any({ &#039;&#039;Buckets&#039;&#039;(i) ≠ ∅ }):&lt;br /&gt;
          &#039;&#039;Bucket&#039;&#039; := first({ &#039;&#039;Buckets&#039;&#039;(i) ≠ ∅ })&lt;br /&gt;
          &#039;&#039;Deleted&#039;&#039; := ∅&lt;br /&gt;
          &#039;&#039;&#039;while&#039;&#039;&#039; &#039;&#039;Bucket&#039;&#039; ≠ ∅:&lt;br /&gt;
              &#039;&#039;Requests&#039;&#039; := FindRequests(&#039;&#039;Bucket&#039;&#039;, &#039;&#039;LightEdges&#039;&#039;)&lt;br /&gt;
              &#039;&#039;Deleted&#039;&#039; := &#039;&#039;Deleted&#039;&#039; ∪ &#039;&#039;Bucket&#039;&#039;&lt;br /&gt;
              &#039;&#039;Bucket&#039;&#039; := ∅&lt;br /&gt;
              RelaxAll(&#039;&#039;Requests&#039;&#039;)&lt;br /&gt;
          RelaxAll(FindRequests(&#039;&#039;Deleted&#039;&#039;, &#039;&#039;HeavyEdges&#039;&#039;))&lt;br /&gt;
          &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; Relax(&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;x&#039;&#039; &amp;lt; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;):&lt;br /&gt;
         &#039;&#039;OldBucket&#039;&#039; := B(⌊&#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) / &#039;&#039;Δ&#039;&#039;⌋)&lt;br /&gt;
         &#039;&#039;NewBucket&#039;&#039; := B(⌊&#039;&#039;x&#039;&#039; / &#039;&#039;Δ&#039;&#039;⌋)&lt;br /&gt;
         &#039;&#039;OldBucket&#039;&#039; := &#039;&#039;OldBucket&#039;&#039; \ {&#039;&#039;v&#039;&#039;}&lt;br /&gt;
         &#039;&#039;NewBucket&#039;&#039; := &#039;&#039;NewBucket&#039;&#039; ∪ {&#039;&#039;v&#039;&#039;}&lt;br /&gt;
         &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;x&#039;&#039;&lt;br /&gt;
         &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; RelaxAll(&#039;&#039;R&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; (&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;) ∈ &#039;&#039;R&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; Relax(&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;)&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; FindRequests(&#039;&#039;V&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;, &#039;&#039;E&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;return&#039;&#039;&#039; { (&#039;&#039;w&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;W&#039;&#039;(&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;)) | &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039; and (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;}&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Средняя последовательная сложность алгоритма на графах со случайными весами &amp;lt;math&amp;gt;O(n + m + dL)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; – максимальный суммарный вес кратчайшего пути, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; – максимальная длина кратчайшего пути.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Средняя время работы параллельного алгоритма на графах со случайными весами на &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; процессорах составляет &amp;lt;math&amp;gt;O((dL + \ln n) \ln n)&amp;lt;/math&amp;gt; со средней работой &amp;lt;math&amp;gt;O(n + m + dL \ln n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Предельные случаи алгоритма Δ-шагания:&lt;br /&gt;
* при &amp;lt;math&amp;gt;\Delta \to 0&amp;lt;/math&amp;gt;: [[алгоритм Дейкстры]];&lt;br /&gt;
* при &amp;lt;math&amp;gt;\Delta \to \infty&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;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#delta-stepping-algorithm delta_stepping_shortest_paths]&amp;lt;/code&amp;gt;).&lt;br /&gt;
{{Buttonlink|http://top53.parallel.ru/algo_results/implementation/25}}&lt;br /&gt;
* Gap: {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/38}}&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;br /&gt;
&lt;br /&gt;
[[en:Δ-stepping algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%BB%D0%B0%D1%81%D1%81%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2&amp;diff=25631</id>
		<title>Классификация алгоритмов</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%BB%D0%B0%D1%81%D1%81%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2&amp;diff=25631"/>
		<updated>2018-06-15T09:06:03Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__TOC__&lt;br /&gt;
= Задачи алгебры =&lt;br /&gt;
==Матричные и векторные операции==&lt;br /&gt;
===Векторные операции===&lt;br /&gt;
# {{level|Суммирование сдваиванием}}&lt;br /&gt;
## {{level|Нахождение суммы элементов массива сдваиванием}}&lt;br /&gt;
## {{level|Нахождение частных сумм элементов массива сдваиванием}}&lt;br /&gt;
# {{level|Равномерная норма вектора, вещественная версия, последовательно-параллельный вариант}}&lt;br /&gt;
# {{level|Скалярное произведение векторов, вещественная версия, последовательно-параллельный вариант}}&lt;br /&gt;
# {{level|Последовательно-параллельный метод суммирования}}&lt;br /&gt;
=== Умножения матриц на вектор ===&lt;br /&gt;
==== Умножения неособенных матриц на вектор ====&lt;br /&gt;
# {{level|Умножение плотной неособенной матрицы на вектор (последовательный вещественный вариант)}}&lt;br /&gt;
==== Умножения матриц специального вида на вектор ====&lt;br /&gt;
# {{level|Преобразование Фурье}}&lt;br /&gt;
## {{level|Быстрое преобразование Фурье для составной размерности}}&lt;br /&gt;
### {{level|Быстрое преобразование Фурье для степеней двойки}}&lt;br /&gt;
#### {{level|Простой алгоритм Кули-Тьюки быстрого преобразования Фурье для степеней двойки}}&lt;br /&gt;
#### {{level|Быстрое преобразование Фурье для чётных степеней двойки}}&lt;br /&gt;
### {{level|Быстрое преобразование Фурье для составной размерности с небольшими простыми делителями (2,3,5,7)}}&lt;br /&gt;
## {{level|Быстрое преобразование Фурье для простой размерности}}&lt;br /&gt;
===Матричные операции===&lt;br /&gt;
# {{level|Умножение плотных матриц}}&lt;br /&gt;
## {{level|Перемножение плотных неособенных матриц (последовательный вещественный вариант)}}&lt;br /&gt;
## {{level|Метод Штрассена}}&lt;br /&gt;
==Разложения матриц==&lt;br /&gt;
{{level|Задача разложения матриц}}&lt;br /&gt;
# {{level|Треугольные разложения}}&lt;br /&gt;
## {{level|Метод Гаусса (нахождение LU-разложения)}}&lt;br /&gt;
### {{level|LU-разложение методом Гаусса без перестановок}}&lt;br /&gt;
#### {{level|LU-разложение методом Гаусса}}&lt;br /&gt;
#### {{level|Компактная схема метода Гаусса и её модификации}}&lt;br /&gt;
##### {{level|Компактная схема метода Гаусса для плотной матрицы}}&lt;br /&gt;
##### {{level|Компактная схема метода Гаусса для трёхдиагональной матрицы и её модификации}}&lt;br /&gt;
###### {{level|Компактная схема метода Гаусса для трёхдиагональной матрицы, последовательный вариант}}&lt;br /&gt;
###### {{level|Алгоритм сдваивания Стоуна для LU-разложения трёхдиагональной матрицы}}&lt;br /&gt;
###### {{level|Последовательно-параллельный алгоритм для LU-разложения трёхдиагональной матрицы}}&lt;br /&gt;
### {{level|LU-разложение методом Гаусса с перестановками}}&lt;br /&gt;
#### {{level|LU-разложение методом Гаусса с выбором ведущего элемента по столбцу}}&lt;br /&gt;
#### {{level|LU-разложение методом Гаусса с выбором ведущего элемента по строке}}&lt;br /&gt;
#### {{level|LU-разложение методом Гаусса с выбором ведущего элемента по главной диагонали}}&lt;br /&gt;
#### {{level|LU-разложение методом Гаусса с выбором ведущего элемента по всей матрице}}&lt;br /&gt;
## {{level|Метод Холецкого (нахождение симметричного треугольного разложения)}}&lt;br /&gt;
### {{level|Разложение Холецкого (метод квадратного корня)}} базовый точечный вещественный вариант для плотной симметричной положительно-определённой матрицы&lt;br /&gt;
# {{level|Известные треугольные разложения для матриц специального вида}}&lt;br /&gt;
# {{level|Унитарно-треугольные разложения}}&lt;br /&gt;
## {{level|QR-разложения плотных неособенных матриц}}&lt;br /&gt;
### {{level|Метод Гивенса (вращений) QR-разложения матрицы}}&lt;br /&gt;
#### {{level|Метод Гивенса (вращений) QR-разложения квадратной матрицы (вещественный точечный вариант)}}&lt;br /&gt;
### {{level|Метод Хаусхолдера (отражений) QR-разложения матрицы}}&lt;br /&gt;
#### {{level|Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный точечный вариант}}&lt;br /&gt;
### {{level|Метод ортогонализации}}&lt;br /&gt;
#### {{level|Классический метод ортогонализации}}&lt;br /&gt;
#### {{level|Метод ортогонализации с переортогонализацией}}&lt;br /&gt;
### {{level|Метод треугольного разложения матрицы Грама}}&lt;br /&gt;
## {{level|Методы QR-разложения плотных хессенберговых матриц}}&lt;br /&gt;
### {{level|Метод Гивенса (вращений) QR-разложения хессенберговой матрицы (вещественный вариант)}}&lt;br /&gt;
### {{level|Метод Хаусхолдера (отражений) QR-разложения хессенберговой матрицы (вещественный вариант)}}&lt;br /&gt;
# {{level|Разложения, содержащие матрицу, подобную исходной}}&lt;br /&gt;
## {{level|Разложения, содержащие хессенбергову матрицу, унитарно подобную исходной}}&lt;br /&gt;
### {{level|Метод Хаусхолдера (отражений) приведения матрицы к хессенберговой (почти треугольной) форме}}&lt;br /&gt;
#### {{level|Классический точечный метод Хаусхолдера (отражений) приведения матрицы к хессенберговой (почти треугольной) форме}}&lt;br /&gt;
### {{level|Метод Гивенса (вращений) приведения матрицы к хессенберговой (почти треугольной) форме}}&lt;br /&gt;
#### {{level|Классический точечный метод Гивенса (вращений) приведения матрицы к хессенберговой (почти треугольной) форме}}&lt;br /&gt;
## {{level|Разложения, содержащие трёхдиагональную матрицу, унитарно подобную исходной}}&lt;br /&gt;
### {{level|Метод Хаусхолдера (отражений) приведения к трёхдиагональному виду}}&lt;br /&gt;
#### {{level|Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду}}&lt;br /&gt;
#### {{level|Метод Хаусхолдера (отражений) для приведения комплексных эрмитовых матриц к трёхдиагональному симметричному виду}}&lt;br /&gt;
### {{level|Метод Гивенса (вращений) приведения матрицы к трёхдиагональной форме}}&lt;br /&gt;
## {{level|Спектральное разложение (нахождение собственных значений и векторов)}}&lt;br /&gt;
# {{level|Разложения с использованием унитарных преобразований, не содержащие матриц, подобной исходной}}&lt;br /&gt;
## {{level|Разложения на две унитарные и одну двухдиагональную матрицы}}&lt;br /&gt;
### {{level|Метод Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме}}&lt;br /&gt;
### {{level|Метод Гивенса (вращений) приведения матрицы к двухдиагональной форме}}&lt;br /&gt;
## {{level|Разложения на две унитарные и одну диагональную матрицы}}&lt;br /&gt;
### {{level|Сингулярное разложение (нахождение сингулярных значений и векторов)}}&lt;br /&gt;
#### {{level|Методы нахождения сингулярных чисел двухдиагональных матриц}}&lt;br /&gt;
##### {{level|Алгоритм dqds нахождения сингулярных чисел двухдиагональной матрицы}}&lt;br /&gt;
###### {{level|Итерация алгоритма dqds}}&lt;br /&gt;
&lt;br /&gt;
==Решение систем линейных уравнений==&lt;br /&gt;
# {{level|Прямые методы решения СЛАУ}}&lt;br /&gt;
## {{level|Linpack benchmark}}&lt;br /&gt;
## {{level|Методы решения СЛАУ с матрицами специального вида}}&lt;br /&gt;
### {{level|Методы решения СЛАУ с треугольными матрицами}}&lt;br /&gt;
#### {{level|Прямая подстановка (вещественный вариант)}}&lt;br /&gt;
#### {{level|Обратная подстановка (вещественный вариант)}}&lt;br /&gt;
#### {{level|Методы решения СЛАУ с двудиагональными матрицами}}&lt;br /&gt;
##### {{level|Прямая и обратная подстановка в СЛАУ с двухдиагональной матрицей}}&lt;br /&gt;
##### {{level|Метод сдваивания Стоуна для решения двудиагональных СЛАУ}}&lt;br /&gt;
##### {{level|Последовательно-параллельный вариант обратной подстановки}}&lt;br /&gt;
### {{level|Методы решения СЛАУ с трёхдиагональными матрицами}}&lt;br /&gt;
#### {{level|Методы, основанные на стандартном LU-разложении матрицы}}&lt;br /&gt;
##### {{level|Прогонка}}&lt;br /&gt;
###### {{level|Прогонка, точечный вариант}}&lt;br /&gt;
###### {{level|Классическая монотонная прогонка, повторный вариант}}&lt;br /&gt;
##### {{level|Метод сдваивания Стоуна}}&lt;br /&gt;
###### {{level|Алгоритм сдваивания Стоуна для LU-разложения трёхдиагональной матрицы}}&lt;br /&gt;
###### {{level|Метод сдваивания Стоуна для решения двудиагональных СЛАУ}}&lt;br /&gt;
##### {{level|Последовательно-параллельный вариант решения трёхдиагональной СЛАУ с LU-разложением и обратными подстановками}}&lt;br /&gt;
#### Другие методы&lt;br /&gt;
##### {{level|Метод редукции}}&lt;br /&gt;
###### {{level|Полный метод редукции}}&lt;br /&gt;
###### {{level|Повторный метод редукции для новой правой части}}&lt;br /&gt;
##### {{level|Встречная прогонка}}&lt;br /&gt;
###### {{level|Встречная прогонка, точечный вариант}}&lt;br /&gt;
###### {{level|Повторная встречная прогонка, точечный вариант}}&lt;br /&gt;
##### {{level|Метод циклической редукции}}&lt;br /&gt;
###### {{level|Полный метод циклической редукции}}&lt;br /&gt;
###### {{level|Повторный метод циклической редукции для новой правой части}}&lt;br /&gt;
##### {{level|Метод окаймления}}&lt;br /&gt;
### Методы решения СЛАУ с блочно-треугольными матрицами&lt;br /&gt;
#### {{level|Блочная прямая подстановка (вещественный вариант)|Блочная прямая подстановка}}&lt;br /&gt;
#### {{level|Блочная обратная подстановка (вещественный вариант)|Блочная обратная подстановка}}&lt;br /&gt;
#### Методы решения СЛАУ с блочно-двухдиагональными матрицами&lt;br /&gt;
##### {{level|Прямая и обратная подстановка в СЛАУ с блочно-двухдиагональной матрицей}}&lt;br /&gt;
##### {{level|Метод сдваивания Стоуна для решения блочно-двухдиагональных СЛАУ}}&lt;br /&gt;
##### {{level|Блочный последовательно-параллельный вариант обратной подстановки для решения блочно-двухдиагональных СЛАУ}}&lt;br /&gt;
### {{level|Методы решения СЛАУ с блочно-трёхдиагональными матрицами}}&lt;br /&gt;
#### Методы, основанные на стандартном LU-разложении матрицы&lt;br /&gt;
##### {{level|Блочная прогонка}}&lt;br /&gt;
##### {{level|Блочный последовательно-параллельный вариант решения с LU-разложением и обратными подстановками}}&lt;br /&gt;
#### Другие методы&lt;br /&gt;
##### {{level|Встречная прогонка, блочный вариант}}&lt;br /&gt;
##### {{level|Блочный метод циклической редукции}}&lt;br /&gt;
##### {{level|Блочный метод окаймления}}&lt;br /&gt;
## {{level|Решения СЛАУ с матрицами специального вида, имеющими известные обратные матрицы}}&lt;br /&gt;
# Итерационные методы решения СЛАУ&lt;br /&gt;
## {{level|High Performance Conjugate Gradient (HPCG) benchmark}}&lt;br /&gt;
## {{level|Стабилизированный метод бисопряженных градиентов (BiCGStab)}}&lt;br /&gt;
## {{level|Алгоритм_Качмажа}}&lt;br /&gt;
&lt;br /&gt;
==Решения спектральных задач==&lt;br /&gt;
# {{level|Спектральное разложение (нахождение собственных значений и векторов)}}&lt;br /&gt;
## {{level|QR-алгоритм}}&lt;br /&gt;
### {{level|QR-алгоритм, используемый в SCALAPACK}}&lt;br /&gt;
#### {{level|Классический точечный метод Хаусхолдера (отражений) приведения матрицы к хессенберговой (почти треугольной) форме}}&lt;br /&gt;
#### {{level|QR-алгоритм для хессенберговой матрицы, используемый в SCALAPACK}}&lt;br /&gt;
### {{level|QR-алгоритм для симметричных матриц, используемый в SCALAPACK}}&lt;br /&gt;
#### {{level|Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду}}&lt;br /&gt;
#### {{level|QR-алгоритм для симметричных трёхдиагональных матриц, используемый в SCALAPACK}}&lt;br /&gt;
### {{level|QR-алгоритм для комплексных эрмитовых матриц, используемый в SCALAPACK}}&lt;br /&gt;
#### {{level|Метод Хаусхолдера (отражений) для приведения комплексных эрмитовых матриц к трёхдиагональному симметричному виду}}&lt;br /&gt;
#### {{level|QR-алгоритм для симметричных трёхдиагональных матриц, используемый в SCALAPACK}}&lt;br /&gt;
## {{level|Метод Якоби (вращений) для решения спектральной задачи у симметричных матриц}}&lt;br /&gt;
### {{level|Классический метод Якоби (вращений) для симметричных матриц с выбором по всей матрице}}&lt;br /&gt;
### {{level|Метод Якоби (вращений) для симметричных матриц с циклическим исключением}}&lt;br /&gt;
### {{level|Метод Якоби (вращений) для симметричных матриц с циклическим исключением и барьерами}}&lt;br /&gt;
## {{level|Метод Ланцоша}}&lt;br /&gt;
### {{level|Алгоритм Ланцоша для точной арифметики (без переортогонализации)}}&lt;br /&gt;
# {{level|Частичная спектральная задача}}&lt;br /&gt;
## {{level|Метод бисекций}}&lt;br /&gt;
# {{level|Сингулярное разложение (нахождение сингулярных значений и векторов)}}&lt;br /&gt;
## {{level|Метод Якоби (вращений) для нахождения сингулярных значений неособенных матриц}}&lt;br /&gt;
### {{level|Метод Якоби (вращений) для нахождения сингулярных значений с циклическим перебором}}&lt;br /&gt;
### {{level|Метод Якоби для нахождения сингулярных значений со специальным подбором вращений}}&lt;br /&gt;
## {{level|QR-алгоритм в приложении к сингулярному разложению}}&lt;br /&gt;
==Алгебра многочленов==&lt;br /&gt;
# {{level|Схема Горнера, вещественная версия, последовательный вариант}}&lt;br /&gt;
&lt;br /&gt;
= Алгоритмы на списках и массивах =&lt;br /&gt;
==Алгоритмы поиска==&lt;br /&gt;
# {{level|Линейный поиск - находит элемент в любом списке|Линейный поиск}}, &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
# {{level|Двоичный поиск - находит элемент в отсортированном списке|Двоичный поиск}}, &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt;&lt;br /&gt;
==Алгоритмы сортировки==&lt;br /&gt;
# {{level|Сортировка с помощью двоичного дерева}}&lt;br /&gt;
# {{level|Сортировка пузырьком}}&lt;br /&gt;
# {{level|Сортировка слиянием (последовательный и параллельный варианты)}}&lt;br /&gt;
==Алгоритмы на графах==&lt;br /&gt;
# Обход графа&lt;br /&gt;
## {{level|Поиск в ширину (BFS)}}&lt;br /&gt;
## {{level|Поиск в глубину (DFS)}}&lt;br /&gt;
# {{level|Поиск кратчайшего пути от одной вершины (SSSP)}} &lt;br /&gt;
## {{level|Поиск в ширину (BFS)}} (для невзвешенных графов)&lt;br /&gt;
## {{level|Алгоритм Дейкстры}} &lt;br /&gt;
## {{level|Алгоритм Беллмана-Форда}} &lt;br /&gt;
## {{level|Алгоритм Δ-шагания}} &lt;br /&gt;
# {{level|Поиск кратчайшего пути для всех пар вершин (APSP)}} &lt;br /&gt;
## {{level|Алгоритм Джонсона}}&lt;br /&gt;
## {{level|Алгоритм Флойда-Уоршелла}}&lt;br /&gt;
# {{level|Поиск транзитивного замыкания орграфа}}&lt;br /&gt;
## {{level|Алгоритм Пурдома}}&lt;br /&gt;
# {{level|Определение диаметра графа}}&lt;br /&gt;
# {{level|Построение минимального остовного дерева (MST)}}&lt;br /&gt;
## {{level|Алгоритм Борувки}}&lt;br /&gt;
## {{level|Алгоритм Крускала}}&lt;br /&gt;
## {{level|Алгоритм Прима}}&lt;br /&gt;
## {{level|Алгоритм GHS}}&lt;br /&gt;
# {{level|Поиск изоморфных подграфов}}&lt;br /&gt;
## {{level|Алгоритм Ульмана}}&lt;br /&gt;
## {{level|Алгоритм VF2}}&lt;br /&gt;
# {{level|Связность в графах}}&lt;br /&gt;
## {{level|Алгоритм Шилоаха-Вишкина поиска компонент связности}}&lt;br /&gt;
## {{level|Система непересекающихся множеств}}&lt;br /&gt;
## {{level|Алгоритм Тарьяна поиска компонент сильной связности}}&lt;br /&gt;
## {{level|Алгоритм DCSC поиска компонент сильной связности}}&lt;br /&gt;
## {{level|Алгоритм Тарьяна поиска компонент двусвязности}}&lt;br /&gt;
## {{level|Алгоритм Тарьяна-Вишкина поиска компонент двусвязности}}&lt;br /&gt;
## {{level|Алгоритм Тарьяна поиска «мостов» в графе}}&lt;br /&gt;
## {{level|Определение вершинной связности графа}}&lt;br /&gt;
## {{level|Алгоритм Габова определения рёберной связности графа}}&lt;br /&gt;
# {{level|Поиск максимального потока в транспортной сети}}&lt;br /&gt;
## {{level|Алгоритм Форда-Фалкерсона}}&lt;br /&gt;
## {{level|Алгоритм проталкивания предпотока}}&lt;br /&gt;
# {{level|Поиск потока минимальной стоимости в транспортной сети}}&lt;br /&gt;
# {{level|Задача о назначениях}}&lt;br /&gt;
## {{level|Венгерский алгоритм}}&lt;br /&gt;
## {{level|Алгоритм аукциона}}&lt;br /&gt;
## {{level|Алгоритм Гопкрофта-Карпа}}&lt;br /&gt;
# {{level|Вычисление betweenness centrality|Вычисление центральности вершин}}&lt;br /&gt;
=Вычислительная геометрия=&lt;br /&gt;
# {{level|Поиск диаметра множества точек}}&lt;br /&gt;
# {{level|Построение выпуклой оболочки набора точек}}&lt;br /&gt;
# {{level|Триангуляция Делоне}}&lt;br /&gt;
# {{level|Диаграмма Вороного}}&lt;br /&gt;
# {{level|Принадлежность точки многоугольнику}}&lt;br /&gt;
# {{level|Пересечения выпуклых многоугольников}} &lt;br /&gt;
# {{level|Пересечение звёздных многоугольников}}&lt;br /&gt;
==Компьютерная графика==&lt;br /&gt;
# {{level|Алгоритмы построения отрезка - алгоритмы для аппроксимации отрезка на дискретной графической поверхности}}&lt;br /&gt;
# {{level|Алгоритм определения видимых частей трёхмерной сцены}}&lt;br /&gt;
# {{level|Трассировка лучей - рендеринг реалистичных изображений}}&lt;br /&gt;
# {{level|Глобальное освещение - рассматривает прямое освещение и отражение от других объектов}}&lt;br /&gt;
&lt;br /&gt;
=Исследование и моделирование компьютеров=&lt;br /&gt;
==Тесты производительности компьютеров==&lt;br /&gt;
# {{level|High Performance Conjugate Gradient (HPCG) benchmark}}&lt;br /&gt;
# {{level|Linpack benchmark}}&lt;br /&gt;
==Алгоритмы моделирования квантовых систем==&lt;br /&gt;
# {{level|Алгоритмы моделирования квантовых вычислений}}&lt;br /&gt;
## {{level|Однокубитное преобразование вектора-состояния}}&lt;br /&gt;
## {{level|Двухкубитное преобразование вектора-состояния}}&lt;br /&gt;
## {{level|Моделирование квантового преобразования Фурье}}&lt;br /&gt;
==Алгоритмы машинного обучения==&lt;br /&gt;
# {{level|Алгоритм k средних (k-means)}}&lt;br /&gt;
===Нейронные сети===&lt;br /&gt;
# {{level|Распознавание образов}}&lt;br /&gt;
## {{level|Распознавание текста}}&lt;br /&gt;
## {{level|Распознавание речи}}&lt;br /&gt;
## {{level|Распознавание лиц}}&lt;br /&gt;
===Алгоритмы теории игр===&lt;br /&gt;
&lt;br /&gt;
= Прикладные задачи из разных областей =&lt;br /&gt;
==Алгоритмы оптимизации==&lt;br /&gt;
# {{level|Линейное программирование}}&lt;br /&gt;
# {{level|Симплекс-метод}}&lt;br /&gt;
# {{level|Метод ветвей и границ}}&lt;br /&gt;
# {{level|Генетические алгоритмы}}&lt;br /&gt;
# {{level|Муравьиные алгоритмы}}&lt;br /&gt;
# {{level|Комбинированные алгоритмы}}&lt;br /&gt;
# {{level|Стохастическое двойственное динамическое программирование (SDDP)}}&lt;br /&gt;
==Решение систем нелинейных уравнений==&lt;br /&gt;
# {{level|Метод Ньютона для систем нелинейных уравнений}}&lt;br /&gt;
==Численные методы интегрирования==&lt;br /&gt;
# {{level|Квадратурные формулы}}&lt;br /&gt;
# {{level|Квадратурные (кубатурные) методы численного интегрирования по отрезку (многомерному кубу)}}&lt;br /&gt;
## [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Метод прямоугольников|Метод прямоугольников]]&lt;br /&gt;
## [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Метод трапеций|Метод трапеций]]&lt;br /&gt;
## [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Метод парабол (метод Симпсона)|Метод парабол (метод Симпсона)]]&lt;br /&gt;
## [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Метод Гаусса|Метод Гаусса]]&lt;br /&gt;
==Алгоритмы решения уравнений математической физики==&lt;br /&gt;
# {{level|Уравнение Пуассона, решение дискретным преобразованием Фурье}}&lt;br /&gt;
=Криптографические алгоритмы=&lt;br /&gt;
# {{level|Метод встречи посередине}}&lt;br /&gt;
=Другие алгоритмы=&lt;br /&gt;
&lt;br /&gt;
[[en:Algorithm classification]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы|*]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=25630</id>
		<title>Алгоритм Дейкстры</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=25630"/>
		<updated>2018-06-15T09:04:02Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-a}}&lt;br /&gt;
&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/5}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
Основные авторы описания: [[Участник:Daryin|А.Н.Дарьин]], [[Участник:VadimVV|Вад.В.Воеводин]] ([[#Описание локальности данных и вычислений|раздел 2.2]])&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Дейкстры&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа.&lt;br /&gt;
Алгоритм Дейкстры (с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt;) выполняется за время &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt; и является асимптотически быстрейшим из известных последовательных алгоритмов для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть уже вычислены все расстояния, не превосходящие некоторого числа &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;, то есть расстояния до вершин из множества &amp;lt;math&amp;gt;V_r = \{ v \in V \mid d(v) \le r \}&amp;lt;/math&amp;gt;. Пусть&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        (v, w) \in \arg\min \{ d(v) + f(e) \mid  v \in V, e = (v, w) \in E \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Тогда &amp;lt;math&amp;gt;d(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; лежит на кратчайшем пути от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Величины &amp;lt;math&amp;gt;d^+(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v \in V_r&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e = (v, w) \in E&amp;lt;/math&amp;gt;, называются &#039;&#039;предполагаемыми расстояниями&#039;&#039; и являются оценкой сверху для настоящих расстояний: &amp;lt;math&amp;gt;d(w) \le d^+(w)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры на каждом шаге находит вершину с наименьшим предполагаемым расстоянием, помечает её как посещённую и обновляет предполагаемые расстояния для всех концов рёбер, исходящих из неё.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основные вычисления связаны с операциями над очередью с приоритетом:&lt;br /&gt;
* извлечение минимального элемента (&amp;lt;code&amp;gt;delete_min&amp;lt;/code&amp;gt;);&lt;br /&gt;
* уменьшение приоритета элемента (&amp;lt;code&amp;gt;decrease_key&amp;lt;/code&amp;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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;Q&#039;&#039; := &#039;&#039;&#039;new&#039;&#039;&#039; priority queue&lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;:&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;v&#039;&#039; = &#039;&#039;u&#039;&#039; &#039;&#039;&#039;then&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := 0 &#039;&#039;&#039;else&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞ &lt;br /&gt;
     &#039;&#039;Q&#039;&#039;.insert(&#039;&#039;v&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;))&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;while&#039;&#039;&#039; &#039;&#039;Q&#039;&#039; ≠ ∅:&lt;br /&gt;
     &#039;&#039;v&#039;&#039; := &#039;&#039;Q&#039;&#039;.delete_min()&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
             &#039;&#039;Q&#039;&#039;.decrease_key(&#039;&#039;w&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;))&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Конкретная реализация алгоритма Дейкстры определяется выбором используемого алгоритма очереди с приоритетом. В простейшем случае это может быть массив или список, поиск минимума в котором требует просмотра всех вершин. Более эффективным является использование кучи; наилучшую известную оценку сложности имеет вариант с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Возможен вариант реализации, когда вершины добавляются в очередь не на этапе инициализации, а в момент первого посещения.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательная сложность алгоритма равна &amp;lt;math&amp;gt;O(C_1 m + C_2n)&amp;lt;/math&amp;gt;, где&lt;br /&gt;
* &amp;lt;math&amp;gt;C_1&amp;lt;/math&amp;gt; – количество операций уменьшения расстояния до вершины;&lt;br /&gt;
* &amp;lt;math&amp;gt;C_2&amp;lt;/math&amp;gt; – количество операций вычисления минимума.&lt;br /&gt;
&lt;br /&gt;
Оригинальный алгоритм Дейкстры использовал в качестве внутренней структуры данных списки, для которых &amp;lt;math&amp;gt;C_1 = O(1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;C_2 = O(n)&amp;lt;/math&amp;gt;, так что общая сложность составляла &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При использовании фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt; время вычисления минимума сокращается до &amp;lt;math&amp;gt;C_2 = O(\ln n)&amp;lt;/math&amp;gt;, так что общая сложность равна &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;, что является асимптотически наилучшим известным результатом для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
Приводится граф алгоритма для базовой реализации алгоритма Дейкстры на списках или массивах.&lt;br /&gt;
[[file:Deikstra.png|thumb|center|600px|Рисунок 1. Граф алгоритма без отображения входных и выходных данных. n=3. Желтым цветом обозначены операции сравнения, зеленым - операции изменения меток вершин, синим - пометка вершины.]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры допускает эффективную параллелизацию&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;, среднее время работы &amp;lt;math&amp;gt;O(n^{1/3}\ln n)&amp;lt;/math&amp;gt; с объёмом вычислений &amp;lt;math&amp;gt;O(n \ln n + m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Дейкстры.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(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;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_1.png|thumb|center|700px|Рисунок 1. Реализация алгоритма Дейкстры. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 1 представлен профиль обращений в память для реализации алгоритма Дейкстры. Первое, что бросается в глаза, – большая разрозненность обращений. В частности, значительные области выше и ниже фрагмента 2 остаются пустыми, при этом сами обращения объединены лишь в небольшие группы. Это говорит о низкой эффективности, поскольку: а) повторные обращения практически отсутствуют либо происходят через значительный промежуток времени; б) расстояния между идущими подряд обращениями может быть очень большим.&lt;br /&gt;
&lt;br /&gt;
Однако при ближайшем рассмотрении может оказаться, что такие участки обладают высокой локальностью и состоят из значительного числа обращений. Более того, на общем профиле есть несколько областей (фрагменты 1 и 2), в которых обращения хорошо локализованы. Необходимо исследовать отдельные участки более подробно.&lt;br /&gt;
&lt;br /&gt;
Перейдем к изучению фрагмента 1 (рис. 2), в рамках которого выполняются обращения к двум небольшим массивам. Можно увидеть, что здесь задействованы только примерно 500 элементов, при этом к ним выполняется около 100 тысяч обращений. Весь профиль составляет около 120 тысяч обращений, поэтому получается, что подавляющая их часть выполняется именно к этим элементам. &lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_2.png|thumb|center|700px|Рисунок 2. Профиль обращений, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Поскольку число элементов невелико, локальность в данном случае заведомо будет достаточно высокой, независимо от структуры самого фрагмента.  Однако на рис. 3, где представлены два участка из фрагмента 1, можно увидеть, что фрагмент в основном состоит из последовательных переборов, при этом данные зачастую задействованы повторно через не очень большие промежутки. Все это позволяет говорить, что данный фрагмент обладает высокой как пространственной, так и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_3.png|thumb|center|700px|Рисунок 3. Профили двух участков фрагмента 1 (выделены зеленым на рис. 2)]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим теперь более подробно фрагмент 2 (рис. 4), в рамках которого выполняются обращения к еще служебному массиву. Здесь профиль состоит из двух этапов. На первом заметен достаточно хаотичный разброс обращений, напоминающий случайный доступ. На втором обращения образуют подобие последовательного перебора.  В целом такой профиль характеризуется очень низкой временной локальностью (повторные обращения практически или полностью отсутствуют) и достаточно низкой пространственной локальностью (из-за случайного доступа на первом этапе).&lt;br /&gt;
&lt;br /&gt;
Заметим, что при этом число задействованных элементов здесь больше, чем во фрагменте 1, однако число обращений гораздо меньше.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_4.png|thumb|center|500px|Рисунок 4. Профиль обращений, фрагмент 2]]&lt;br /&gt;
&lt;br /&gt;
Далее остаются для рассмотрения два массива (область между фрагментами 1 и 2 и область ниже фрагмента 2). Характер обращений к этим массивам во многом похож, поэтому достаточно изучить более подробно только один из них. &lt;br /&gt;
&lt;br /&gt;
Фрагмент 3 рассмотрен на рис. 5. Этот участок отражаeт достаточно большую область, что не позволяет проанализировать профиль вплоть до отдельных обращений, однако здесь этого и не требуется. Из данного фрагмента видно, что основу профиля составляют участки с последовательным (или с небольшим шагом) перебором, состоящие из небольшого числа элементов – выделенный желтым самый большой участок фрагмента состоит всего из пары сотен обращений. При этом между разными участками расстояние может быть существенным. Все это говорит об очень низкой локальности (как пространственной, так и временной) в случае двух рассматриваемых массивов.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_5.png|thumb|center|500px|Рисунок 5. Профиль обращений, фрагмент 3]]&lt;br /&gt;
&lt;br /&gt;
В целом, несмотря на положительный вклад массивов из фрагмента 1, локальность общего профиля должна быть достаточно низкой, вследствие неэффективного использования данных в остальной части профиля.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Основной фрагмент реализации, на основе которого были получены количественные оценки, приведен [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/benchmarks/dijkstra/dijkstra.h здесь] (функция Kernel). Условия запуска описаны [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/README.md здесь].&lt;br /&gt;
&lt;br /&gt;
Первая оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью достаточно низка. Это неудивительно, поскольку реализации алгоритмов над графами почти всегда обладают низкой эффективностью вследствие нерегулярности доступа к данным, что мы и увидели при анализе профиля обращений.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
Вторая характеристика – cvg – предназначена для получения более машинно-независимой оценки локальности. Она определяет, насколько часто в программе необходимо подтягивать данные в кэш-память. Соответственно, чем меньше значение cvg, тем реже это нужно делать, тем лучше локальность. &lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения cvg для того же набора реализаций, отсортированные по убыванию (чем меньше cvg, тем в общем случае выше локальность). Можно увидеть, что в данном случае значение cvg хорошо коррелирует с оценкой производительности и отражает низкую локальность, что соответствует выводам, сделанным при качественной оценке локальности.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_cvg.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки cvg]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot;&amp;lt;ref name=&amp;quot;Lom&amp;quot;&amp;gt;Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&amp;lt;/ref&amp;gt; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [4, 8 : 128] со значениями квадрата целого числа;&lt;br /&gt;
*	размер графа [16000:64000] с шагом 16000.&lt;br /&gt;
&lt;br /&gt;
На следующем рисунке приведен график [[Глоссарий#Производительность|производительности]] выбранной реализации алгоритма в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra perf.png|thumb|center|700px|Рисунок 8. Параллельная реализация алгоритма. Изменение производительности в зависимости от числа процессоров и размера области.]]&lt;br /&gt;
&lt;br /&gt;
В силу особенностей параллельной реализации алгоритма производительность в целом достаточно низкая и с ростом числа процессов увеличивается медленно, а при приближении к числу процессов 128 начинает уменьшаться.&lt;br /&gt;
Это объясняется использованием коллективных операций на каждой итерации алгоритма и тем, что затраты на коммуникационные обмены существенно возрастают с ростом числа использованных процессов. Вычисления на каждом процессе проходят достаточно быстро и потому декомпозиция графа слабо компенсирует эффект от затрат на коммуникационные обмены. &lt;br /&gt;
&lt;br /&gt;
[https://code.google.com/p/prir-dijkstra-mpi/source/browse/trunk/project/dijkstra_mpi.c Исследованная параллельная реализация на языке C]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация алгоритма Дейкстры.  Все результаты получены на суперкомпьютере &amp;quot;Ломоносов&amp;quot;. Использовались процессоры Intel Xeon X5570 с пиковой производительностью в 94 Гфлопс, а также компилятор intel 13.1.0. &lt;br /&gt;
На рисунках показана эффективность реализации алгоритма Дейкстры  на 32 процессах.&lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra CPU User.png|thumb|center|700px|Рисунок 9. График загрузки CPU при выполнении алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы уровень загрузки составляет около 50%. Это указывает на равномерную загруженность вычислениями процессоров, при использовании 8 процессов на вычислительный узел и без использования Hyper Threading. &lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra Flops.png|thumb|center|700px|Рисунок 10. График операций с плавающей точкой в секунду при выполнении алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На Рисунке 10 показан график количества операций с плавающей точкой в секунду. На графике видна общая очень низкая производительность вычислений около 250 Kфлопс в пике и около 150 Кфлопс в среднем по всем узлам. Это указывает то, что в программе почти все вычисления производятся с целыми числами. &lt;br /&gt;
 [[file:Dijkstra L1.png|thumb|center|700px|Рисунок 11. График кэш-промахов L1 в секунду при работе алгоритма Дейкстры]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов очень большое для нескольких ядер и находится на уровне 15 млн/сек (в пике до 60 млн/сек), что указывает на интенсивные вычисления в части процессов. В среднем по всем узлам значения значительно ниже (около 9 млн/сек). Это указывает на неравномерное распределение вычислений.&lt;br /&gt;
 [[file:Dijkstra L3.png|thumb|center|700px|Рисунок 12. График кэш-промахов L3 в секунду при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов третьего уровня видно, что число промахов очень невелико и находится на уровне 1,2 млн/сек, однако в среднем по всем узлам значения около 0,5 млн/сек. Соотношение кэш промахов L1|L3 для процессов с высокой производительностью доходит до 60, однако в среднем около 30. Это указывает на очень хорошую локальность вычислений как у части процессов, так и для всех в среднем, и это является признаком высокой производительности. &lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra Mem Load.png|thumb|center|700px|Рисунок 13. График количества чтений из оперативной памяти при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике обращений в память видна достаточно типичная картина для таких приложений. Активность чтения достаточно низкая, что в совокупности с низкими значениями кэш-промахов L3 указывает на хорошую локальность. Хорошая локальность приложения также указывает на то, что значение около 1 млн/сек для задачи является результатом высокой производительности вычислений, хотя и присутствует неравномерность между процессами.&lt;br /&gt;
 [[file:Dijkstra Mem Store.png|thumb|center|700px|Рисунок 14. График количества записей в оперативную память при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике записей в память видна похожая картина неравномерности вычислений, при которой одновременно активно выполняют запись только несколько процессов. Это коррелирует с другими графиками выполнения. Стоит отметить достаточно низкое число обращений на запись в память. Это указывает на хорошую организацию вычислений и достаточно эффективную работу с памятью.&lt;br /&gt;
 [[file:Dijkstra Inf DATA.png|thumb|center|700px|Рисунок 15. График скорости передачи по сети Infiniband в байт/сек при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно высокая скорость передачи данных в байтах в секунду. Это говорит о том, что процессы между собой обмениваются интенсивно и вероятно достаточно малыми порциями данных, потому как производительность вычислений высока. Стоит отметить, что скорость передачи отличается между процессами, что указывает на дисбаланс вычислений.&lt;br /&gt;
 [[file:Dijkstra Inf PCKTS.png|thumb|center|700px|Рисунок 16. График скорости передачи по сети Infiniband в пакетах/сек при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается крайне высокая интенсивность передачи данных. Это говорит о том, что, вероятно, процессы обмениваются не очень существенными объемами данных, но очень интенсивно. Используются коллективные операции на каждом шаге с небольшими порциями данных, что объясняет такую картину.  Также наблюдается меньший дизбаланс между процессами, чем наблюдаемый в графиках использования памяти, вычислений и передачи данных в байтах/сек. Это указывает на то, что процессы обмениваются по алгоритму одинаковым числом пакетов, однако получают разные объемы данных и ведут неравномерные вычисления. &lt;br /&gt;
 [[file:Dijkstra Load ABG.png|thumb|center|700px|Рисунок 17. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе алгоритма Дейкстры]]&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно и приблизительно равняется 8. Это свидетельствует о стабильной работе программы с загруженными вычислениями всеми узлами. Это указывает на очень рациональную и статичную загрузку аппаратных ресурсов процессами. И показывает достаточно хорошую эффективность выполняемой реализации.&lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала достаточно эффективно, и стабильно. Использование памяти  очень интенсивное, а использование коммуникационной среды крайне интенсивное, при этом объемы передаваемых данных не являются высокими. Это указывает на требовательность к латентности коммуникационной среды алгоритмической части программы. &lt;br /&gt;
Низкая эффективность связана, судя по всему, с достаточно высоким объемом пересылок на каждом процессе и с интенсивными обменами сообщениями.&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths.html dijkstra_shortest_paths]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths_no_color_map.html dijkstra_shortest_paths_no_color_map]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. &lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]: {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/14}}&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#eager-dijkstra-s-algorithm eager_dijkstra_shortest_paths]&amp;lt;/code&amp;gt; – непосредственная реализация алгоритма Дейкстры;&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#crauser-et-al-s-algorithm crauser_et_al_shortest_paths]&amp;lt;/code&amp;gt; – реализация алгоритма Дейкстры в виде алгоритма из статьи Краузера и др.&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt; &lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.single_source_dijkstra.html single_source_dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python/C++: [https://networkit.iti.kit.edu NetworKit] (класс &amp;lt;code&amp;gt;[https://networkit.iti.kit.edu/data/uploads/docs/NetworKit-Doc/python/html/graph.html#networkit.graph.Dijkstra networkit.graph.Dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Gap {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/38}}&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En:Dijkstra&#039;s algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=25629</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=25629"/>
		<updated>2018-06-15T08:59:04Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/6}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм Беллмана-Форда&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(|V||E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;N/A, max O(|V|) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(|V| + |E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(|V|^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(|V||E|)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(|E| + |V|\ln(|V|))&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В самом начале производится присваивание &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;d(v) = \infty&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\forall v \ne u&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Далее происходит &amp;lt;math&amp;gt;|V|-1&amp;lt;/math&amp;gt; итерация, в ходе каждой из которых производится релаксация всех рёбер графа.&lt;br /&gt;
&lt;br /&gt;
Структуру можно описать следующим образом:&lt;br /&gt;
&lt;br /&gt;
1.	Инициализация: всем вершинам присваивается предполагаемое расстояние &amp;lt;math&amp;gt;t(v)=\infty&amp;lt;/math&amp;gt;, кроме вершины-источника, для которой &amp;lt;math&amp;gt;t(u)=0&amp;lt;/math&amp;gt; .&lt;br /&gt;
&lt;br /&gt;
2.	Релаксация множества рёбер &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
а)	Для каждого ребра &amp;lt;math&amp;gt;e=(v,z) \in E&amp;lt;/math&amp;gt; вычисляется новое предполагаемое расстояние &amp;lt;math&amp;gt;t^&#039; (z)=t(v)+ w(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
б)	Если &amp;lt;math&amp;gt;t^&#039; (z)&amp;lt; t(z)&amp;lt;/math&amp;gt;, то происходит присваивание &amp;lt;math&amp;gt;t(z) := t&#039; (z)&amp;lt;/math&amp;gt; (релаксация ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; ).&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм производит релаксацию всех рёбер графа до тех пор, пока на очередной итерации происходит релаксация хотя бы одного ребра.&lt;br /&gt;
&lt;br /&gt;
Если на &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt;-й итерации всё ещё производилась релаксацию рёбер, то в графе присутствует цикл отрицательной длины. Ребро &amp;lt;math&amp;gt;e=(v,z)&amp;lt;/math&amp;gt;, лежащее на таком цикле, может быть найдено проверкой следующего условия (проверяется для всех рёбер за линейное время): &amp;lt;math&amp;gt;t(v)+w(e)&amp;lt;d(z)&amp;lt;/math&amp;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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
 &#039;&#039;d&#039;&#039;(&#039;&#039;u&#039;&#039;) = 0&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;i&#039;&#039; &#039;&#039;&#039;from&#039;&#039;&#039; 1 &#039;&#039;&#039;to&#039;&#039;&#039; |&#039;&#039;V&#039;&#039;| - 1:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;w&#039;&#039;, &#039;&#039;v&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Алгоритм выполняет &amp;lt;math&amp;gt;|V|-1&amp;lt;/math&amp;gt; итерацию, на каждой из которых происходит релаксация &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбер. Таким образом, общий объём работы составляет &amp;lt;math&amp;gt;O(|V||E|)&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 представлен информационный граф алгоритма, демонстрирующий описанные уровни параллелизма.&lt;br /&gt;
&lt;br /&gt;
[[file:APSP.png|thumb|center|700px|Рисунок 1. Информационный граф обобщенного алгоритма Беллмана-Форда.]]&lt;br /&gt;
&lt;br /&gt;
На приведенном далее информационном графе нижний уровень параллелизма обозначен в горизонтальных плоскостях. Множество всех плоскостей представляет собой верхний уровень параллелизма (операции в каждой плоскости могут выполняться параллельно).&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма расположен на уровнях [2] и [3], соответствующим операциям инициализации массива дистанций [2] и обновления массива c использованием данных массива ребер [3]. Операция [4] - проверка того, были ли изменения на последней итерации и выход из цикла, если таковых не было.&lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма, как уже говорилось, заключается в параллельном подсчете дистанций для различных вершин-источников, и на рисунке отмечен разными плоскостями.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм обладает значительным ресурсом параллелизма. Во-первых, поиск кратчайших путей от различных вершин может производиться независимо для каждой из вершин (параллельные вертикальные плоскости на рисунке 1). Во-вторых, поиск кратчайших путей от фиксированной вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; также может выполняться параллельно: инициализация начальных путей [2] требует &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; параллельных операции, релаксация каждого ребра требует &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt; параллельных операции. &lt;br /&gt;
&lt;br /&gt;
Таким образом, при наличии &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt; процессоров алгоритм завершит работу максимум за &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; шагов. В реальности, шагов обычно требуется меньше, а именно &amp;lt;math&amp;gt;O(r)&amp;lt;/math&amp;gt; -(максимальная длина среди всех кратчайших путей от выбранной вершины-источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Таким образом, ширина ярусно-параллельной формы алгоритма равна &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;, высота ЯПФ - &amp;lt;math&amp;gt;O(r) | r &amp;lt; |V|&amp;lt;/math&amp;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;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V| + |E|)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Алгоритм может распознавать наличие отрицательных циклов в графе. Ребро &amp;lt;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(|E|)&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;
[[file:bellman_ford_1.png|thumb|center|700px|Рисунок 2. Алгоритм Беллмана-Форда. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации алгоритма Беллмана-Форда. Первое, что сразу стоит отметить – число обращений в память гораздо больше числа задействованных данных. Это говорит о частом повторном использовании одних и тех же данных, что обычно приводит к высокой временной локальности. Далее, видна явная регулярная структура производимых обращений в память, видны повторяющиеся итерации работы алгоритма. Практически все обращения образуют фрагменты, похожие на последовательный перебор, кроме самой верхней части, где наблюдается более сложная структура.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим более детально отдельные фрагменты общего профиля, чтобы лучше разобраться в его структуре. На рис. 3 представлен фрагмент 1 (выделен на рис. 2), на котором показаны первые 500 обращений в память (отметим, что другой наклон двух последовательных переборов связан с измененным отношением сторон в рассматриваемой области). Данный рисунок показывает, что выделенные желтым части 1 и 2 являются практически идентичными последовательными переборами; отличие между ними только в том, что в части 1 обращения выполняются в два раза чаще, поэтому на рис. 3 эта часть представлена большим числом точек. Как мы знаем, подобные профили характеризуются высокой пространственной и низкой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим более интересный фрагмент 2, отмеченный на рис. 2 (см. рис. 4). Здесь можно снова увидеть подтверждение регулярности обращений в нижней области профиля, однако верхняя область явно устроена гораздо сложнее; хотя и здесь просматривается регулярность. В частности, также видны те же самые итерации, в которых здесь можно выделить большие последовательности обращений к одним и тем же данным. Пример такого поведения, оптимального с точки зрения локальности, выделен на рисунке желтым. &lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_3.png|thumb|center|700px|Рисунок 4. Профиль обращений, фрагмент 2]]&lt;br /&gt;
&lt;br /&gt;
Чтобы понять структуру обращений в память в верхней части, можно рассмотреть ее еще подробнее. Приведем визуализацию небольшой области фрагмента 2, выделенной на рис. 4 зеленым. Однако в данном случае дальнейшее приближение (рис. 5) не привносит большей ясности: видна нерегулярная структура внутри итерации, характер которой достаточно сложно описать. Но в данном случае этого и не требуется – можно заметить, что по вертикали отложено всего 15 элементов, при этом обращений к ним выполняется гораздо больше. Независимо от структуры обращений, такой профиль обладает очень высокой как пространственной, так и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_4.png|thumb|center|700px|Рисунок 5. Небольшая часть фрагмента 2]]&lt;br /&gt;
&lt;br /&gt;
А так как основная масса обращений приходится именно на фрагмент 2, можно утверждать, что и весь общий профиль обладает высокой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что по производительности работы с памятью данная реализация алгоритма показывает очень хорошие результаты. В частности, значение daps сравнимо с оценкой для теста Linpack, который известен высокой эффективностью взаимодействия с подсистемой памяти. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая алгоритм поиска кратчайших путей, состоит из двух частей: части, отвечающей за общую координацию вычислений, а также параллельные вычисления на многоядерных CPU, и GPU части, отвечающей только за вычисления на графическом ускорителе.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Алгоритм обладает значительным потенциалом масштабируемости, так как каждое ребро обрабатывается независимо и можно поручить каждому вычислительному процессу свою часть рёбер графа. Узким местом является доступ к разделяемому всеми процессами массиву расстояний. Алгоритм позволяет ослабить требования к синхронизации данных этого массива между процессами (когда один процесс может не сразу увидеть новое значение расстояния, записанное другим процессом), за счёт, может быть, большего количества глобальных итераций.&lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Беллмана-Форда согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости вширь реализации алгоритма Беллмана-Форда.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера.&lt;br /&gt;
&lt;br /&gt;
[[file:Сильная_масштабируемость_вширь_алгоритма_Беллмана-Форда.png|thumb|center|700px|Рисунок 7. Параллельная реализация алгоритма Беллмана-Форда масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация алгоритма Беллмана-Форда, реализованная для CPU.  Все результаты получены на суперкомпьютере «Ломоносов-2». Использовались процессоры Intel Xeon E5-2697v3, задача решалась для графа большого размера на одном узле. &lt;br /&gt;
На рисунках показана эффективность реализации алгоритма Беллмана-Форда, запуск проводился на 1 узле для графа 2^27, выполнялась 1 итерация.&lt;br /&gt;
[[file:Bellman-Ford cpu.png|thumb|center|700px|Рисунок 8. График загрузки CPU при выполнении алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы не загружены и средний уровень загрузки составляет около 5%. Это достаточно неэффективная картина даже для программ, запущенных c использованием одного ядра в реализации. &lt;br /&gt;
&lt;br /&gt;
[[file:Bellman-Ford LoadAvg.png|thumb|center|700px|Рисунок 9. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно на уровне 1. Это указывает на постоянную загрузку аппаратных ресурсов не более чем 1 процессом, однако это число для узла слишком мало, что может указывать на не очень рациональное использование ресурсов.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford L1.png|thumb|center|700px|Рисунок 10. График кэш-промахов L1 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов очень высокое и находится на уровне 140 млн/сек. Это очень высокий показатель, показывающий потенциальную причину неэффективности.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford L2.png|thumb|center|700px|Рисунок 11. График кэш-промахов L2 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов второго уровня видно, что число таких промахов тоже крайне высокое и находится на уровне 140 млн/сек, что указывает на крайне неэффективную работу с памятью.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford-L3.png|thumb|center|700px|Рисунок 12. График кэш-промахов L3 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов последнего уровня видно, что число промахов тоже достаточно большое и составляет около 30 млн/сек по всем узлам. Это указывает на то, что задача очень плохо укладывается в кэш-память, и программа постоянно работает с оперативной памятью, что объясняется очень большим размером использованного графа.&lt;br /&gt;
&lt;br /&gt;
[[file:Bellman-Ford Inf Data.png|thumb|center|700px|Рисунок 13. График скорости передачи по сети Infiniband в байт/сек при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно высокая интенсивность использования сети на первом этапе. Это объясняется программной логикой, которая предполагает чтение графа из файла с диска, коммуникации с которым происходят на Ломоносов-2 через выделенную для этих задач сеть Infiniband.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford Inf Pckts.png|thumb|center|700px|Рисунок 14. График скорости передачи по сети Infiniband в пакетах/сек при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается аналогичная картина очень высокой интенсивности на первом этапе выполнения задачи. Далее сеть почти не используется.&lt;br /&gt;
 &lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала стабильно интенсивно,  однако очень неэффективно использовала память из-за очень большого размера графа.&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* OpenMP Stinger {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/10}}&lt;br /&gt;
* Nvidia nvGraph {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/19}}  &lt;br /&gt;
* Graph500 MPI {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/24}}&lt;br /&gt;
* Ligra {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/41}}&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En:Bellman-Ford algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=25628</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=25628"/>
		<updated>2018-06-15T08:58:22Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/6}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм Беллмана-Форда&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(|V||E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;N/A, max O(|V|) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(|V| + |E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(|V|^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(|V||E|)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(|E| + |V|\ln(|V|))&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В самом начале производится присваивание &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;d(v) = \infty&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\forall v \ne u&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Далее происходит &amp;lt;math&amp;gt;|V|-1&amp;lt;/math&amp;gt; итерация, в ходе каждой из которых производится релаксация всех рёбер графа.&lt;br /&gt;
&lt;br /&gt;
Структуру можно описать следующим образом:&lt;br /&gt;
&lt;br /&gt;
1.	Инициализация: всем вершинам присваивается предполагаемое расстояние &amp;lt;math&amp;gt;t(v)=\infty&amp;lt;/math&amp;gt;, кроме вершины-источника, для которой &amp;lt;math&amp;gt;t(u)=0&amp;lt;/math&amp;gt; .&lt;br /&gt;
&lt;br /&gt;
2.	Релаксация множества рёбер &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
а)	Для каждого ребра &amp;lt;math&amp;gt;e=(v,z) \in E&amp;lt;/math&amp;gt; вычисляется новое предполагаемое расстояние &amp;lt;math&amp;gt;t^&#039; (z)=t(v)+ w(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
б)	Если &amp;lt;math&amp;gt;t^&#039; (z)&amp;lt; t(z)&amp;lt;/math&amp;gt;, то происходит присваивание &amp;lt;math&amp;gt;t(z) := t&#039; (z)&amp;lt;/math&amp;gt; (релаксация ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; ).&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм производит релаксацию всех рёбер графа до тех пор, пока на очередной итерации происходит релаксация хотя бы одного ребра.&lt;br /&gt;
&lt;br /&gt;
Если на &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt;-й итерации всё ещё производилась релаксацию рёбер, то в графе присутствует цикл отрицательной длины. Ребро &amp;lt;math&amp;gt;e=(v,z)&amp;lt;/math&amp;gt;, лежащее на таком цикле, может быть найдено проверкой следующего условия (проверяется для всех рёбер за линейное время): &amp;lt;math&amp;gt;t(v)+w(e)&amp;lt;d(z)&amp;lt;/math&amp;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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
 &#039;&#039;d&#039;&#039;(&#039;&#039;u&#039;&#039;) = 0&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;i&#039;&#039; &#039;&#039;&#039;from&#039;&#039;&#039; 1 &#039;&#039;&#039;to&#039;&#039;&#039; |&#039;&#039;V&#039;&#039;| - 1:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;w&#039;&#039;, &#039;&#039;v&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Алгоритм выполняет &amp;lt;math&amp;gt;|V|-1&amp;lt;/math&amp;gt; итерацию, на каждой из которых происходит релаксация &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбер. Таким образом, общий объём работы составляет &amp;lt;math&amp;gt;O(|V||E|)&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 представлен информационный граф алгоритма, демонстрирующий описанные уровни параллелизма.&lt;br /&gt;
&lt;br /&gt;
[[file:APSP.png|thumb|center|700px|Рисунок 1. Информационный граф обобщенного алгоритма Беллмана-Форда.]]&lt;br /&gt;
&lt;br /&gt;
На приведенном далее информационном графе нижний уровень параллелизма обозначен в горизонтальных плоскостях. Множество всех плоскостей представляет собой верхний уровень параллелизма (операции в каждой плоскости могут выполняться параллельно).&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма расположен на уровнях [2] и [3], соответствующим операциям инициализации массива дистанций [2] и обновления массива c использованием данных массива ребер [3]. Операция [4] - проверка того, были ли изменения на последней итерации и выход из цикла, если таковых не было.&lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма, как уже говорилось, заключается в параллельном подсчете дистанций для различных вершин-источников, и на рисунке отмечен разными плоскостями.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм обладает значительным ресурсом параллелизма. Во-первых, поиск кратчайших путей от различных вершин может производиться независимо для каждой из вершин (параллельные вертикальные плоскости на рисунке 1). Во-вторых, поиск кратчайших путей от фиксированной вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; также может выполняться параллельно: инициализация начальных путей [2] требует &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; параллельных операции, релаксация каждого ребра требует &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt; параллельных операции. &lt;br /&gt;
&lt;br /&gt;
Таким образом, при наличии &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt; процессоров алгоритм завершит работу максимум за &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; шагов. В реальности, шагов обычно требуется меньше, а именно &amp;lt;math&amp;gt;O(r)&amp;lt;/math&amp;gt; -(максимальная длина среди всех кратчайших путей от выбранной вершины-источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Таким образом, ширина ярусно-параллельной формы алгоритма равна &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;, высота ЯПФ - &amp;lt;math&amp;gt;O(r) | r &amp;lt; |V|&amp;lt;/math&amp;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;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V| + |E|)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Алгоритм может распознавать наличие отрицательных циклов в графе. Ребро &amp;lt;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(|E|)&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;
[[file:bellman_ford_1.png|thumb|center|700px|Рисунок 2. Алгоритм Беллмана-Форда. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации алгоритма Беллмана-Форда. Первое, что сразу стоит отметить – число обращений в память гораздо больше числа задействованных данных. Это говорит о частом повторном использовании одних и тех же данных, что обычно приводит к высокой временной локальности. Далее, видна явная регулярная структура производимых обращений в память, видны повторяющиеся итерации работы алгоритма. Практически все обращения образуют фрагменты, похожие на последовательный перебор, кроме самой верхней части, где наблюдается более сложная структура.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим более детально отдельные фрагменты общего профиля, чтобы лучше разобраться в его структуре. На рис. 3 представлен фрагмент 1 (выделен на рис. 2), на котором показаны первые 500 обращений в память (отметим, что другой наклон двух последовательных переборов связан с измененным отношением сторон в рассматриваемой области). Данный рисунок показывает, что выделенные желтым части 1 и 2 являются практически идентичными последовательными переборами; отличие между ними только в том, что в части 1 обращения выполняются в два раза чаще, поэтому на рис. 3 эта часть представлена большим числом точек. Как мы знаем, подобные профили характеризуются высокой пространственной и низкой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим более интересный фрагмент 2, отмеченный на рис. 2 (см. рис. 4). Здесь можно снова увидеть подтверждение регулярности обращений в нижней области профиля, однако верхняя область явно устроена гораздо сложнее; хотя и здесь просматривается регулярность. В частности, также видны те же самые итерации, в которых здесь можно выделить большие последовательности обращений к одним и тем же данным. Пример такого поведения, оптимального с точки зрения локальности, выделен на рисунке желтым. &lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_3.png|thumb|center|700px|Рисунок 4. Профиль обращений, фрагмент 2]]&lt;br /&gt;
&lt;br /&gt;
Чтобы понять структуру обращений в память в верхней части, можно рассмотреть ее еще подробнее. Приведем визуализацию небольшой области фрагмента 2, выделенной на рис. 4 зеленым. Однако в данном случае дальнейшее приближение (рис. 5) не привносит большей ясности: видна нерегулярная структура внутри итерации, характер которой достаточно сложно описать. Но в данном случае этого и не требуется – можно заметить, что по вертикали отложено всего 15 элементов, при этом обращений к ним выполняется гораздо больше. Независимо от структуры обращений, такой профиль обладает очень высокой как пространственной, так и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_4.png|thumb|center|700px|Рисунок 5. Небольшая часть фрагмента 2]]&lt;br /&gt;
&lt;br /&gt;
А так как основная масса обращений приходится именно на фрагмент 2, можно утверждать, что и весь общий профиль обладает высокой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что по производительности работы с памятью данная реализация алгоритма показывает очень хорошие результаты. В частности, значение daps сравнимо с оценкой для теста Linpack, который известен высокой эффективностью взаимодействия с подсистемой памяти. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая алгоритм поиска кратчайших путей, состоит из двух частей: части, отвечающей за общую координацию вычислений, а также параллельные вычисления на многоядерных CPU, и GPU части, отвечающей только за вычисления на графическом ускорителе.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Алгоритм обладает значительным потенциалом масштабируемости, так как каждое ребро обрабатывается независимо и можно поручить каждому вычислительному процессу свою часть рёбер графа. Узким местом является доступ к разделяемому всеми процессами массиву расстояний. Алгоритм позволяет ослабить требования к синхронизации данных этого массива между процессами (когда один процесс может не сразу увидеть новое значение расстояния, записанное другим процессом), за счёт, может быть, большего количества глобальных итераций.&lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Беллмана-Форда согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости вширь реализации алгоритма Беллмана-Форда.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера.&lt;br /&gt;
&lt;br /&gt;
[[file:Сильная_масштабируемость_вширь_алгоритма_Беллмана-Форда.png|thumb|center|700px|Рисунок 7. Параллельная реализация алгоритма Беллмана-Форда масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация алгоритма Беллмана-Форда, реализованная для CPU.  Все результаты получены на суперкомпьютере «Ломоносов-2». Использовались процессоры Intel Xeon E5-2697v3, задача решалась для графа большого размера на одном узле. &lt;br /&gt;
На рисунках показана эффективность реализации алгоритма Беллмана-Форда, запуск проводился на 1 узле для графа 2^27, выполнялась 1 итерация.&lt;br /&gt;
[[file:Bellman-Ford cpu.png|thumb|center|700px|Рисунок 8. График загрузки CPU при выполнении алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы не загружены и средний уровень загрузки составляет около 5%. Это достаточно неэффективная картина даже для программ, запущенных c использованием одного ядра в реализации. &lt;br /&gt;
&lt;br /&gt;
[[file:Bellman-Ford LoadAvg.png|thumb|center|700px|Рисунок 9. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно на уровне 1. Это указывает на постоянную загрузку аппаратных ресурсов не более чем 1 процессом, однако это число для узла слишком мало, что может указывать на не очень рациональное использование ресурсов.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford L1.png|thumb|center|700px|Рисунок 10. График кэш-промахов L1 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов очень высокое и находится на уровне 140 млн/сек. Это очень высокий показатель, показывающий потенциальную причину неэффективности.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford L2.png|thumb|center|700px|Рисунок 11. График кэш-промахов L2 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов второго уровня видно, что число таких промахов тоже крайне высокое и находится на уровне 140 млн/сек, что указывает на крайне неэффективную работу с памятью.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford-L3.png|thumb|center|700px|Рисунок 12. График кэш-промахов L3 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов последнего уровня видно, что число промахов тоже достаточно большое и составляет около 30 млн/сек по всем узлам. Это указывает на то, что задача очень плохо укладывается в кэш-память, и программа постоянно работает с оперативной памятью, что объясняется очень большим размером использованного графа.&lt;br /&gt;
&lt;br /&gt;
[[file:Bellman-Ford Inf Data.png|thumb|center|700px|Рисунок 13. График скорости передачи по сети Infiniband в байт/сек при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно высокая интенсивность использования сети на первом этапе. Это объясняется программной логикой, которая предполагает чтение графа из файла с диска, коммуникации с которым происходят на Ломоносов-2 через выделенную для этих задач сеть Infiniband.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford Inf Pckts.png|thumb|center|700px|Рисунок 14. График скорости передачи по сети Infiniband в пакетах/сек при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается аналогичная картина очень высокой интенсивности на первом этапе выполнения задачи. Далее сеть почти не используется.&lt;br /&gt;
 &lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала стабильно интенсивно,  однако очень неэффективно использовала память из-за очень большого размера графа.&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* OpenMP Stinger {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/10}}&lt;br /&gt;
* Nvidia nvGraph {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/19}}  &lt;br /&gt;
* Graph500 MPI {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/24}}&lt;br /&gt;
* Ligra {{http://top53.parallel.ru/algo_results/implementation/41}}&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En:Bellman-Ford algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=25627</id>
		<title>Алгоритм Дейкстры</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=25627"/>
		<updated>2018-06-15T08:40:45Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-a}}&lt;br /&gt;
&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/5}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
Основные авторы описания: [[Участник:Daryin|А.Н.Дарьин]], [[Участник:VadimVV|Вад.В.Воеводин]] ([[#Описание локальности данных и вычислений|раздел 2.2]])&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Дейкстры&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа.&lt;br /&gt;
Алгоритм Дейкстры (с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt;) выполняется за время &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt; и является асимптотически быстрейшим из известных последовательных алгоритмов для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть уже вычислены все расстояния, не превосходящие некоторого числа &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;, то есть расстояния до вершин из множества &amp;lt;math&amp;gt;V_r = \{ v \in V \mid d(v) \le r \}&amp;lt;/math&amp;gt;. Пусть&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        (v, w) \in \arg\min \{ d(v) + f(e) \mid  v \in V, e = (v, w) \in E \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Тогда &amp;lt;math&amp;gt;d(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; лежит на кратчайшем пути от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Величины &amp;lt;math&amp;gt;d^+(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v \in V_r&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e = (v, w) \in E&amp;lt;/math&amp;gt;, называются &#039;&#039;предполагаемыми расстояниями&#039;&#039; и являются оценкой сверху для настоящих расстояний: &amp;lt;math&amp;gt;d(w) \le d^+(w)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры на каждом шаге находит вершину с наименьшим предполагаемым расстоянием, помечает её как посещённую и обновляет предполагаемые расстояния для всех концов рёбер, исходящих из неё.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основные вычисления связаны с операциями над очередью с приоритетом:&lt;br /&gt;
* извлечение минимального элемента (&amp;lt;code&amp;gt;delete_min&amp;lt;/code&amp;gt;);&lt;br /&gt;
* уменьшение приоритета элемента (&amp;lt;code&amp;gt;decrease_key&amp;lt;/code&amp;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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;Q&#039;&#039; := &#039;&#039;&#039;new&#039;&#039;&#039; priority queue&lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;:&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;v&#039;&#039; = &#039;&#039;u&#039;&#039; &#039;&#039;&#039;then&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := 0 &#039;&#039;&#039;else&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞ &lt;br /&gt;
     &#039;&#039;Q&#039;&#039;.insert(&#039;&#039;v&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;))&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;while&#039;&#039;&#039; &#039;&#039;Q&#039;&#039; ≠ ∅:&lt;br /&gt;
     &#039;&#039;v&#039;&#039; := &#039;&#039;Q&#039;&#039;.delete_min()&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
             &#039;&#039;Q&#039;&#039;.decrease_key(&#039;&#039;w&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;))&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Конкретная реализация алгоритма Дейкстры определяется выбором используемого алгоритма очереди с приоритетом. В простейшем случае это может быть массив или список, поиск минимума в котором требует просмотра всех вершин. Более эффективным является использование кучи; наилучшую известную оценку сложности имеет вариант с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Возможен вариант реализации, когда вершины добавляются в очередь не на этапе инициализации, а в момент первого посещения.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательная сложность алгоритма равна &amp;lt;math&amp;gt;O(C_1 m + C_2n)&amp;lt;/math&amp;gt;, где&lt;br /&gt;
* &amp;lt;math&amp;gt;C_1&amp;lt;/math&amp;gt; – количество операций уменьшения расстояния до вершины;&lt;br /&gt;
* &amp;lt;math&amp;gt;C_2&amp;lt;/math&amp;gt; – количество операций вычисления минимума.&lt;br /&gt;
&lt;br /&gt;
Оригинальный алгоритм Дейкстры использовал в качестве внутренней структуры данных списки, для которых &amp;lt;math&amp;gt;C_1 = O(1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;C_2 = O(n)&amp;lt;/math&amp;gt;, так что общая сложность составляла &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При использовании фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt; время вычисления минимума сокращается до &amp;lt;math&amp;gt;C_2 = O(\ln n)&amp;lt;/math&amp;gt;, так что общая сложность равна &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;, что является асимптотически наилучшим известным результатом для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
Приводится граф алгоритма для базовой реализации алгоритма Дейкстры на списках или массивах.&lt;br /&gt;
[[file:Deikstra.png|thumb|center|600px|Рисунок 1. Граф алгоритма без отображения входных и выходных данных. n=3. Желтым цветом обозначены операции сравнения, зеленым - операции изменения меток вершин, синим - пометка вершины.]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры допускает эффективную параллелизацию&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;, среднее время работы &amp;lt;math&amp;gt;O(n^{1/3}\ln n)&amp;lt;/math&amp;gt; с объёмом вычислений &amp;lt;math&amp;gt;O(n \ln n + m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Дейкстры.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(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;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_1.png|thumb|center|700px|Рисунок 1. Реализация алгоритма Дейкстры. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 1 представлен профиль обращений в память для реализации алгоритма Дейкстры. Первое, что бросается в глаза, – большая разрозненность обращений. В частности, значительные области выше и ниже фрагмента 2 остаются пустыми, при этом сами обращения объединены лишь в небольшие группы. Это говорит о низкой эффективности, поскольку: а) повторные обращения практически отсутствуют либо происходят через значительный промежуток времени; б) расстояния между идущими подряд обращениями может быть очень большим.&lt;br /&gt;
&lt;br /&gt;
Однако при ближайшем рассмотрении может оказаться, что такие участки обладают высокой локальностью и состоят из значительного числа обращений. Более того, на общем профиле есть несколько областей (фрагменты 1 и 2), в которых обращения хорошо локализованы. Необходимо исследовать отдельные участки более подробно.&lt;br /&gt;
&lt;br /&gt;
Перейдем к изучению фрагмента 1 (рис. 2), в рамках которого выполняются обращения к двум небольшим массивам. Можно увидеть, что здесь задействованы только примерно 500 элементов, при этом к ним выполняется около 100 тысяч обращений. Весь профиль составляет около 120 тысяч обращений, поэтому получается, что подавляющая их часть выполняется именно к этим элементам. &lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_2.png|thumb|center|700px|Рисунок 2. Профиль обращений, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Поскольку число элементов невелико, локальность в данном случае заведомо будет достаточно высокой, независимо от структуры самого фрагмента.  Однако на рис. 3, где представлены два участка из фрагмента 1, можно увидеть, что фрагмент в основном состоит из последовательных переборов, при этом данные зачастую задействованы повторно через не очень большие промежутки. Все это позволяет говорить, что данный фрагмент обладает высокой как пространственной, так и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_3.png|thumb|center|700px|Рисунок 3. Профили двух участков фрагмента 1 (выделены зеленым на рис. 2)]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим теперь более подробно фрагмент 2 (рис. 4), в рамках которого выполняются обращения к еще служебному массиву. Здесь профиль состоит из двух этапов. На первом заметен достаточно хаотичный разброс обращений, напоминающий случайный доступ. На втором обращения образуют подобие последовательного перебора.  В целом такой профиль характеризуется очень низкой временной локальностью (повторные обращения практически или полностью отсутствуют) и достаточно низкой пространственной локальностью (из-за случайного доступа на первом этапе).&lt;br /&gt;
&lt;br /&gt;
Заметим, что при этом число задействованных элементов здесь больше, чем во фрагменте 1, однако число обращений гораздо меньше.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_4.png|thumb|center|500px|Рисунок 4. Профиль обращений, фрагмент 2]]&lt;br /&gt;
&lt;br /&gt;
Далее остаются для рассмотрения два массива (область между фрагментами 1 и 2 и область ниже фрагмента 2). Характер обращений к этим массивам во многом похож, поэтому достаточно изучить более подробно только один из них. &lt;br /&gt;
&lt;br /&gt;
Фрагмент 3 рассмотрен на рис. 5. Этот участок отражаeт достаточно большую область, что не позволяет проанализировать профиль вплоть до отдельных обращений, однако здесь этого и не требуется. Из данного фрагмента видно, что основу профиля составляют участки с последовательным (или с небольшим шагом) перебором, состоящие из небольшого числа элементов – выделенный желтым самый большой участок фрагмента состоит всего из пары сотен обращений. При этом между разными участками расстояние может быть существенным. Все это говорит об очень низкой локальности (как пространственной, так и временной) в случае двух рассматриваемых массивов.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_5.png|thumb|center|500px|Рисунок 5. Профиль обращений, фрагмент 3]]&lt;br /&gt;
&lt;br /&gt;
В целом, несмотря на положительный вклад массивов из фрагмента 1, локальность общего профиля должна быть достаточно низкой, вследствие неэффективного использования данных в остальной части профиля.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Основной фрагмент реализации, на основе которого были получены количественные оценки, приведен [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/benchmarks/dijkstra/dijkstra.h здесь] (функция Kernel). Условия запуска описаны [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/README.md здесь].&lt;br /&gt;
&lt;br /&gt;
Первая оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью достаточно низка. Это неудивительно, поскольку реализации алгоритмов над графами почти всегда обладают низкой эффективностью вследствие нерегулярности доступа к данным, что мы и увидели при анализе профиля обращений.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
Вторая характеристика – cvg – предназначена для получения более машинно-независимой оценки локальности. Она определяет, насколько часто в программе необходимо подтягивать данные в кэш-память. Соответственно, чем меньше значение cvg, тем реже это нужно делать, тем лучше локальность. &lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения cvg для того же набора реализаций, отсортированные по убыванию (чем меньше cvg, тем в общем случае выше локальность). Можно увидеть, что в данном случае значение cvg хорошо коррелирует с оценкой производительности и отражает низкую локальность, что соответствует выводам, сделанным при качественной оценке локальности.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_cvg.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки cvg]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot;&amp;lt;ref name=&amp;quot;Lom&amp;quot;&amp;gt;Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&amp;lt;/ref&amp;gt; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [4, 8 : 128] со значениями квадрата целого числа;&lt;br /&gt;
*	размер графа [16000:64000] с шагом 16000.&lt;br /&gt;
&lt;br /&gt;
На следующем рисунке приведен график [[Глоссарий#Производительность|производительности]] выбранной реализации алгоритма в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra perf.png|thumb|center|700px|Рисунок 8. Параллельная реализация алгоритма. Изменение производительности в зависимости от числа процессоров и размера области.]]&lt;br /&gt;
&lt;br /&gt;
В силу особенностей параллельной реализации алгоритма производительность в целом достаточно низкая и с ростом числа процессов увеличивается медленно, а при приближении к числу процессов 128 начинает уменьшаться.&lt;br /&gt;
Это объясняется использованием коллективных операций на каждой итерации алгоритма и тем, что затраты на коммуникационные обмены существенно возрастают с ростом числа использованных процессов. Вычисления на каждом процессе проходят достаточно быстро и потому декомпозиция графа слабо компенсирует эффект от затрат на коммуникационные обмены. &lt;br /&gt;
&lt;br /&gt;
[https://code.google.com/p/prir-dijkstra-mpi/source/browse/trunk/project/dijkstra_mpi.c Исследованная параллельная реализация на языке C]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация алгоритма Дейкстры.  Все результаты получены на суперкомпьютере &amp;quot;Ломоносов&amp;quot;. Использовались процессоры Intel Xeon X5570 с пиковой производительностью в 94 Гфлопс, а также компилятор intel 13.1.0. &lt;br /&gt;
На рисунках показана эффективность реализации алгоритма Дейкстры  на 32 процессах.&lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra CPU User.png|thumb|center|700px|Рисунок 9. График загрузки CPU при выполнении алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы уровень загрузки составляет около 50%. Это указывает на равномерную загруженность вычислениями процессоров, при использовании 8 процессов на вычислительный узел и без использования Hyper Threading. &lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra Flops.png|thumb|center|700px|Рисунок 10. График операций с плавающей точкой в секунду при выполнении алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На Рисунке 10 показан график количества операций с плавающей точкой в секунду. На графике видна общая очень низкая производительность вычислений около 250 Kфлопс в пике и около 150 Кфлопс в среднем по всем узлам. Это указывает то, что в программе почти все вычисления производятся с целыми числами. &lt;br /&gt;
 [[file:Dijkstra L1.png|thumb|center|700px|Рисунок 11. График кэш-промахов L1 в секунду при работе алгоритма Дейкстры]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов очень большое для нескольких ядер и находится на уровне 15 млн/сек (в пике до 60 млн/сек), что указывает на интенсивные вычисления в части процессов. В среднем по всем узлам значения значительно ниже (около 9 млн/сек). Это указывает на неравномерное распределение вычислений.&lt;br /&gt;
 [[file:Dijkstra L3.png|thumb|center|700px|Рисунок 12. График кэш-промахов L3 в секунду при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов третьего уровня видно, что число промахов очень невелико и находится на уровне 1,2 млн/сек, однако в среднем по всем узлам значения около 0,5 млн/сек. Соотношение кэш промахов L1|L3 для процессов с высокой производительностью доходит до 60, однако в среднем около 30. Это указывает на очень хорошую локальность вычислений как у части процессов, так и для всех в среднем, и это является признаком высокой производительности. &lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra Mem Load.png|thumb|center|700px|Рисунок 13. График количества чтений из оперативной памяти при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике обращений в память видна достаточно типичная картина для таких приложений. Активность чтения достаточно низкая, что в совокупности с низкими значениями кэш-промахов L3 указывает на хорошую локальность. Хорошая локальность приложения также указывает на то, что значение около 1 млн/сек для задачи является результатом высокой производительности вычислений, хотя и присутствует неравномерность между процессами.&lt;br /&gt;
 [[file:Dijkstra Mem Store.png|thumb|center|700px|Рисунок 14. График количества записей в оперативную память при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике записей в память видна похожая картина неравномерности вычислений, при которой одновременно активно выполняют запись только несколько процессов. Это коррелирует с другими графиками выполнения. Стоит отметить достаточно низкое число обращений на запись в память. Это указывает на хорошую организацию вычислений и достаточно эффективную работу с памятью.&lt;br /&gt;
 [[file:Dijkstra Inf DATA.png|thumb|center|700px|Рисунок 15. График скорости передачи по сети Infiniband в байт/сек при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно высокая скорость передачи данных в байтах в секунду. Это говорит о том, что процессы между собой обмениваются интенсивно и вероятно достаточно малыми порциями данных, потому как производительность вычислений высока. Стоит отметить, что скорость передачи отличается между процессами, что указывает на дисбаланс вычислений.&lt;br /&gt;
 [[file:Dijkstra Inf PCKTS.png|thumb|center|700px|Рисунок 16. График скорости передачи по сети Infiniband в пакетах/сек при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается крайне высокая интенсивность передачи данных. Это говорит о том, что, вероятно, процессы обмениваются не очень существенными объемами данных, но очень интенсивно. Используются коллективные операции на каждом шаге с небольшими порциями данных, что объясняет такую картину.  Также наблюдается меньший дизбаланс между процессами, чем наблюдаемый в графиках использования памяти, вычислений и передачи данных в байтах/сек. Это указывает на то, что процессы обмениваются по алгоритму одинаковым числом пакетов, однако получают разные объемы данных и ведут неравномерные вычисления. &lt;br /&gt;
 [[file:Dijkstra Load ABG.png|thumb|center|700px|Рисунок 17. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе алгоритма Дейкстры]]&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно и приблизительно равняется 8. Это свидетельствует о стабильной работе программы с загруженными вычислениями всеми узлами. Это указывает на очень рациональную и статичную загрузку аппаратных ресурсов процессами. И показывает достаточно хорошую эффективность выполняемой реализации.&lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала достаточно эффективно, и стабильно. Использование памяти  очень интенсивное, а использование коммуникационной среды крайне интенсивное, при этом объемы передаваемых данных не являются высокими. Это указывает на требовательность к латентности коммуникационной среды алгоритмической части программы. &lt;br /&gt;
Низкая эффективность связана, судя по всему, с достаточно высоким объемом пересылок на каждом процессе и с интенсивными обменами сообщениями.&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths.html dijkstra_shortest_paths]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths_no_color_map.html dijkstra_shortest_paths_no_color_map]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. &lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]: {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/14}}&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#eager-dijkstra-s-algorithm eager_dijkstra_shortest_paths]&amp;lt;/code&amp;gt; – непосредственная реализация алгоритма Дейкстры;&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#crauser-et-al-s-algorithm crauser_et_al_shortest_paths]&amp;lt;/code&amp;gt; – реализация алгоритма Дейкстры в виде алгоритма из статьи Краузера и др.&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt; &lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.single_source_dijkstra.html single_source_dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python/C++: [https://networkit.iti.kit.edu NetworKit] (класс &amp;lt;code&amp;gt;[https://networkit.iti.kit.edu/data/uploads/docs/NetworKit-Doc/python/html/graph.html#networkit.graph.Dijkstra networkit.graph.Dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En:Dijkstra&#039;s algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%CE%94-%D1%88%D0%B0%D0%B3%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=25626</id>
		<title>Алгоритм Δ-шагания</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%CE%94-%D1%88%D0%B0%D0%B3%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=25626"/>
		<updated>2018-06-15T08:35:32Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-a}}&lt;br /&gt;
&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/2}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм дельта-шагания&#039;&#039;&#039;&amp;lt;ref&amp;gt;Meyer, U, and P Sanders. “Δ-Stepping: a Parallelizable Shortest Path Algorithm.” Journal of Algorithms 49, no. 1 (October 2003): 114–52. doi:10.1016/S0196-6774(03)00076-2.&amp;lt;/ref&amp;gt; (англ. Δ-Stepping) предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм изначально проектировался с целью эффективной параллелизации.&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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;W&#039;&#039;;&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;;&lt;br /&gt;
   параметр &#039;&#039;Δ&#039;&#039; &amp;gt; 0.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; DeltaStepping(&#039;&#039;V&#039;&#039;, &#039;&#039;E&#039;&#039;, &#039;&#039;W&#039;&#039;, &#039;&#039;u&#039;&#039;, &#039;&#039;Δ&#039;&#039;):&lt;br /&gt;
      &#039;&#039;LightEdges&#039;&#039; := { e ∈ E | W(e) ≤ Δ }&lt;br /&gt;
      &#039;&#039;HeavyEdges&#039;&#039; := { e ∈ E | W(e) &amp;gt; Δ }&lt;br /&gt;
      &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
      Relax(&#039;&#039;u&#039;&#039;, 0)&lt;br /&gt;
      &#039;&#039;&#039;while&#039;&#039;&#039; any({ &#039;&#039;Buckets&#039;&#039;(i) ≠ ∅ }):&lt;br /&gt;
          &#039;&#039;Bucket&#039;&#039; := first({ &#039;&#039;Buckets&#039;&#039;(i) ≠ ∅ })&lt;br /&gt;
          &#039;&#039;Deleted&#039;&#039; := ∅&lt;br /&gt;
          &#039;&#039;&#039;while&#039;&#039;&#039; &#039;&#039;Bucket&#039;&#039; ≠ ∅:&lt;br /&gt;
              &#039;&#039;Requests&#039;&#039; := FindRequests(&#039;&#039;Bucket&#039;&#039;, &#039;&#039;LightEdges&#039;&#039;)&lt;br /&gt;
              &#039;&#039;Deleted&#039;&#039; := &#039;&#039;Deleted&#039;&#039; ∪ &#039;&#039;Bucket&#039;&#039;&lt;br /&gt;
              &#039;&#039;Bucket&#039;&#039; := ∅&lt;br /&gt;
              RelaxAll(&#039;&#039;Requests&#039;&#039;)&lt;br /&gt;
          RelaxAll(FindRequests(&#039;&#039;Deleted&#039;&#039;, &#039;&#039;HeavyEdges&#039;&#039;))&lt;br /&gt;
          &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; Relax(&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;x&#039;&#039; &amp;lt; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;):&lt;br /&gt;
         &#039;&#039;OldBucket&#039;&#039; := B(⌊&#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) / &#039;&#039;Δ&#039;&#039;⌋)&lt;br /&gt;
         &#039;&#039;NewBucket&#039;&#039; := B(⌊&#039;&#039;x&#039;&#039; / &#039;&#039;Δ&#039;&#039;⌋)&lt;br /&gt;
         &#039;&#039;OldBucket&#039;&#039; := &#039;&#039;OldBucket&#039;&#039; \ {&#039;&#039;v&#039;&#039;}&lt;br /&gt;
         &#039;&#039;NewBucket&#039;&#039; := &#039;&#039;NewBucket&#039;&#039; ∪ {&#039;&#039;v&#039;&#039;}&lt;br /&gt;
         &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;x&#039;&#039;&lt;br /&gt;
         &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; RelaxAll(&#039;&#039;R&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; (&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;) ∈ &#039;&#039;R&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; Relax(&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;)&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; FindRequests(&#039;&#039;V&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;, &#039;&#039;E&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;return&#039;&#039;&#039; { (&#039;&#039;w&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;W&#039;&#039;(&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;)) | &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039; and (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;}&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Средняя последовательная сложность алгоритма на графах со случайными весами &amp;lt;math&amp;gt;O(n + m + dL)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; – максимальный суммарный вес кратчайшего пути, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; – максимальная длина кратчайшего пути.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Средняя время работы параллельного алгоритма на графах со случайными весами на &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; процессорах составляет &amp;lt;math&amp;gt;O((dL + \ln n) \ln n)&amp;lt;/math&amp;gt; со средней работой &amp;lt;math&amp;gt;O(n + m + dL \ln n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Предельные случаи алгоритма Δ-шагания:&lt;br /&gt;
* при &amp;lt;math&amp;gt;\Delta \to 0&amp;lt;/math&amp;gt;: [[алгоритм Дейкстры]];&lt;br /&gt;
* при &amp;lt;math&amp;gt;\Delta \to \infty&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;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#delta-stepping-algorithm delta_stepping_shortest_paths]&amp;lt;/code&amp;gt;).&lt;br /&gt;
{{Buttonlink|http://top53.parallel.ru/algo_results/implementation/25}}&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;br /&gt;
&lt;br /&gt;
[[en:Δ-stepping algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=25625</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=25625"/>
		<updated>2018-06-15T08:31:08Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/6}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм Беллмана-Форда&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(|V||E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;N/A, max O(|V|) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(|V| + |E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(|V|^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(|V||E|)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(|E| + |V|\ln(|V|))&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В самом начале производится присваивание &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;d(v) = \infty&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\forall v \ne u&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Далее происходит &amp;lt;math&amp;gt;|V|-1&amp;lt;/math&amp;gt; итерация, в ходе каждой из которых производится релаксация всех рёбер графа.&lt;br /&gt;
&lt;br /&gt;
Структуру можно описать следующим образом:&lt;br /&gt;
&lt;br /&gt;
1.	Инициализация: всем вершинам присваивается предполагаемое расстояние &amp;lt;math&amp;gt;t(v)=\infty&amp;lt;/math&amp;gt;, кроме вершины-источника, для которой &amp;lt;math&amp;gt;t(u)=0&amp;lt;/math&amp;gt; .&lt;br /&gt;
&lt;br /&gt;
2.	Релаксация множества рёбер &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
а)	Для каждого ребра &amp;lt;math&amp;gt;e=(v,z) \in E&amp;lt;/math&amp;gt; вычисляется новое предполагаемое расстояние &amp;lt;math&amp;gt;t^&#039; (z)=t(v)+ w(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
б)	Если &amp;lt;math&amp;gt;t^&#039; (z)&amp;lt; t(z)&amp;lt;/math&amp;gt;, то происходит присваивание &amp;lt;math&amp;gt;t(z) := t&#039; (z)&amp;lt;/math&amp;gt; (релаксация ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; ).&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм производит релаксацию всех рёбер графа до тех пор, пока на очередной итерации происходит релаксация хотя бы одного ребра.&lt;br /&gt;
&lt;br /&gt;
Если на &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt;-й итерации всё ещё производилась релаксацию рёбер, то в графе присутствует цикл отрицательной длины. Ребро &amp;lt;math&amp;gt;e=(v,z)&amp;lt;/math&amp;gt;, лежащее на таком цикле, может быть найдено проверкой следующего условия (проверяется для всех рёбер за линейное время): &amp;lt;math&amp;gt;t(v)+w(e)&amp;lt;d(z)&amp;lt;/math&amp;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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
 &#039;&#039;d&#039;&#039;(&#039;&#039;u&#039;&#039;) = 0&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;i&#039;&#039; &#039;&#039;&#039;from&#039;&#039;&#039; 1 &#039;&#039;&#039;to&#039;&#039;&#039; |&#039;&#039;V&#039;&#039;| - 1:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;w&#039;&#039;, &#039;&#039;v&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Алгоритм выполняет &amp;lt;math&amp;gt;|V|-1&amp;lt;/math&amp;gt; итерацию, на каждой из которых происходит релаксация &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбер. Таким образом, общий объём работы составляет &amp;lt;math&amp;gt;O(|V||E|)&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 представлен информационный граф алгоритма, демонстрирующий описанные уровни параллелизма.&lt;br /&gt;
&lt;br /&gt;
[[file:APSP.png|thumb|center|700px|Рисунок 1. Информационный граф обобщенного алгоритма Беллмана-Форда.]]&lt;br /&gt;
&lt;br /&gt;
На приведенном далее информационном графе нижний уровень параллелизма обозначен в горизонтальных плоскостях. Множество всех плоскостей представляет собой верхний уровень параллелизма (операции в каждой плоскости могут выполняться параллельно).&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма расположен на уровнях [2] и [3], соответствующим операциям инициализации массива дистанций [2] и обновления массива c использованием данных массива ребер [3]. Операция [4] - проверка того, были ли изменения на последней итерации и выход из цикла, если таковых не было.&lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма, как уже говорилось, заключается в параллельном подсчете дистанций для различных вершин-источников, и на рисунке отмечен разными плоскостями.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм обладает значительным ресурсом параллелизма. Во-первых, поиск кратчайших путей от различных вершин может производиться независимо для каждой из вершин (параллельные вертикальные плоскости на рисунке 1). Во-вторых, поиск кратчайших путей от фиксированной вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; также может выполняться параллельно: инициализация начальных путей [2] требует &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; параллельных операции, релаксация каждого ребра требует &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt; параллельных операции. &lt;br /&gt;
&lt;br /&gt;
Таким образом, при наличии &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt; процессоров алгоритм завершит работу максимум за &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; шагов. В реальности, шагов обычно требуется меньше, а именно &amp;lt;math&amp;gt;O(r)&amp;lt;/math&amp;gt; -(максимальная длина среди всех кратчайших путей от выбранной вершины-источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Таким образом, ширина ярусно-параллельной формы алгоритма равна &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;, высота ЯПФ - &amp;lt;math&amp;gt;O(r) | r &amp;lt; |V|&amp;lt;/math&amp;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;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V| + |E|)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Алгоритм может распознавать наличие отрицательных циклов в графе. Ребро &amp;lt;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(|E|)&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;
[[file:bellman_ford_1.png|thumb|center|700px|Рисунок 2. Алгоритм Беллмана-Форда. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации алгоритма Беллмана-Форда. Первое, что сразу стоит отметить – число обращений в память гораздо больше числа задействованных данных. Это говорит о частом повторном использовании одних и тех же данных, что обычно приводит к высокой временной локальности. Далее, видна явная регулярная структура производимых обращений в память, видны повторяющиеся итерации работы алгоритма. Практически все обращения образуют фрагменты, похожие на последовательный перебор, кроме самой верхней части, где наблюдается более сложная структура.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим более детально отдельные фрагменты общего профиля, чтобы лучше разобраться в его структуре. На рис. 3 представлен фрагмент 1 (выделен на рис. 2), на котором показаны первые 500 обращений в память (отметим, что другой наклон двух последовательных переборов связан с измененным отношением сторон в рассматриваемой области). Данный рисунок показывает, что выделенные желтым части 1 и 2 являются практически идентичными последовательными переборами; отличие между ними только в том, что в части 1 обращения выполняются в два раза чаще, поэтому на рис. 3 эта часть представлена большим числом точек. Как мы знаем, подобные профили характеризуются высокой пространственной и низкой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим более интересный фрагмент 2, отмеченный на рис. 2 (см. рис. 4). Здесь можно снова увидеть подтверждение регулярности обращений в нижней области профиля, однако верхняя область явно устроена гораздо сложнее; хотя и здесь просматривается регулярность. В частности, также видны те же самые итерации, в которых здесь можно выделить большие последовательности обращений к одним и тем же данным. Пример такого поведения, оптимального с точки зрения локальности, выделен на рисунке желтым. &lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_3.png|thumb|center|700px|Рисунок 4. Профиль обращений, фрагмент 2]]&lt;br /&gt;
&lt;br /&gt;
Чтобы понять структуру обращений в память в верхней части, можно рассмотреть ее еще подробнее. Приведем визуализацию небольшой области фрагмента 2, выделенной на рис. 4 зеленым. Однако в данном случае дальнейшее приближение (рис. 5) не привносит большей ясности: видна нерегулярная структура внутри итерации, характер которой достаточно сложно описать. Но в данном случае этого и не требуется – можно заметить, что по вертикали отложено всего 15 элементов, при этом обращений к ним выполняется гораздо больше. Независимо от структуры обращений, такой профиль обладает очень высокой как пространственной, так и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_4.png|thumb|center|700px|Рисунок 5. Небольшая часть фрагмента 2]]&lt;br /&gt;
&lt;br /&gt;
А так как основная масса обращений приходится именно на фрагмент 2, можно утверждать, что и весь общий профиль обладает высокой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что по производительности работы с памятью данная реализация алгоритма показывает очень хорошие результаты. В частности, значение daps сравнимо с оценкой для теста Linpack, который известен высокой эффективностью взаимодействия с подсистемой памяти. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая алгоритм поиска кратчайших путей, состоит из двух частей: части, отвечающей за общую координацию вычислений, а также параллельные вычисления на многоядерных CPU, и GPU части, отвечающей только за вычисления на графическом ускорителе.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Алгоритм обладает значительным потенциалом масштабируемости, так как каждое ребро обрабатывается независимо и можно поручить каждому вычислительному процессу свою часть рёбер графа. Узким местом является доступ к разделяемому всеми процессами массиву расстояний. Алгоритм позволяет ослабить требования к синхронизации данных этого массива между процессами (когда один процесс может не сразу увидеть новое значение расстояния, записанное другим процессом), за счёт, может быть, большего количества глобальных итераций.&lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Беллмана-Форда согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости вширь реализации алгоритма Беллмана-Форда.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера.&lt;br /&gt;
&lt;br /&gt;
[[file:Сильная_масштабируемость_вширь_алгоритма_Беллмана-Форда.png|thumb|center|700px|Рисунок 7. Параллельная реализация алгоритма Беллмана-Форда масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация алгоритма Беллмана-Форда, реализованная для CPU.  Все результаты получены на суперкомпьютере «Ломоносов-2». Использовались процессоры Intel Xeon E5-2697v3, задача решалась для графа большого размера на одном узле. &lt;br /&gt;
На рисунках показана эффективность реализации алгоритма Беллмана-Форда, запуск проводился на 1 узле для графа 2^27, выполнялась 1 итерация.&lt;br /&gt;
[[file:Bellman-Ford cpu.png|thumb|center|700px|Рисунок 8. График загрузки CPU при выполнении алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы не загружены и средний уровень загрузки составляет около 5%. Это достаточно неэффективная картина даже для программ, запущенных c использованием одного ядра в реализации. &lt;br /&gt;
&lt;br /&gt;
[[file:Bellman-Ford LoadAvg.png|thumb|center|700px|Рисунок 9. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно на уровне 1. Это указывает на постоянную загрузку аппаратных ресурсов не более чем 1 процессом, однако это число для узла слишком мало, что может указывать на не очень рациональное использование ресурсов.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford L1.png|thumb|center|700px|Рисунок 10. График кэш-промахов L1 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов очень высокое и находится на уровне 140 млн/сек. Это очень высокий показатель, показывающий потенциальную причину неэффективности.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford L2.png|thumb|center|700px|Рисунок 11. График кэш-промахов L2 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов второго уровня видно, что число таких промахов тоже крайне высокое и находится на уровне 140 млн/сек, что указывает на крайне неэффективную работу с памятью.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford-L3.png|thumb|center|700px|Рисунок 12. График кэш-промахов L3 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов последнего уровня видно, что число промахов тоже достаточно большое и составляет около 30 млн/сек по всем узлам. Это указывает на то, что задача очень плохо укладывается в кэш-память, и программа постоянно работает с оперативной памятью, что объясняется очень большим размером использованного графа.&lt;br /&gt;
&lt;br /&gt;
[[file:Bellman-Ford Inf Data.png|thumb|center|700px|Рисунок 13. График скорости передачи по сети Infiniband в байт/сек при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно высокая интенсивность использования сети на первом этапе. Это объясняется программной логикой, которая предполагает чтение графа из файла с диска, коммуникации с которым происходят на Ломоносов-2 через выделенную для этих задач сеть Infiniband.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford Inf Pckts.png|thumb|center|700px|Рисунок 14. График скорости передачи по сети Infiniband в пакетах/сек при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается аналогичная картина очень высокой интенсивности на первом этапе выполнения задачи. Далее сеть почти не используется.&lt;br /&gt;
 &lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала стабильно интенсивно,  однако очень неэффективно использовала память из-за очень большого размера графа.&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En:Bellman-Ford algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%BE%D1%82_%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B_(SSSP)&amp;diff=25624</id>
		<title>Поиск кратчайшего пути от одной вершины (SSSP)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%BE%D1%82_%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B_(SSSP)&amp;diff=25624"/>
		<updated>2018-06-15T08:26:40Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-p}}&lt;br /&gt;
&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/task/29}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
== Постановка задачи ==&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Последовательность &amp;lt;math&amp;gt;P(u, v)&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_1 = (u, w_1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e_2 = (w_1, w_2)&amp;lt;/math&amp;gt;, …, &amp;lt;math&amp;gt;e_k = (w_{k-1}, v)&amp;lt;/math&amp;gt; называется &#039;&#039;путём&#039;&#039;, идущим от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к вершине &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Суммарный вес этого пути равен&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f(P(u, v)) = \sum_{i = 1}^k f(e_i).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Требуется для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, достижимой из вершины-источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, указать путь &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt;, имеющий наименьший возможный суммарный вес:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f^*(v) = f(P^*(u, v)) = \min f(P(u, v)).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Название задачи на английском языке – «Single Source Shortest Path» (SSSP).&lt;br /&gt;
&lt;br /&gt;
== Варианты задачи ==&lt;br /&gt;
&lt;br /&gt;
Если требуется найти кратчайшие пути не от одной, а от всех вершин - см. [[Поиск кратчайшего пути для всех пар вершин (APSP)|поиск кратчайшего пути для всех пар вершин]].&lt;br /&gt;
&lt;br /&gt;
Задача может быть поставлена как для ориентированного, так и для неориентированного графа. Приведённая постановка задачи применима в обоих случаях.&lt;br /&gt;
&lt;br /&gt;
В зависимости от ограничений на возможные значения весов различают следующие случаи.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Единичные веса&#039;&#039;&#039;. В этом случае задача существенно упрощается и может быть решена за линейное время [[Поиск в ширину (BFS)|алгоритмом поиска вширь]].&lt;br /&gt;
* &#039;&#039;&#039;Положительные целые веса&#039;&#039;&#039;. Задача также может быть решена за линейное время&amp;lt;ref name=thorup&amp;gt;Thorup, Mikkel. “Undirected Single-Source Shortest Paths with Positive Integer Weights in Linear Time.” Journal of the ACM 46, no. 3 (May 1, 1999): 362–94. doi:10.1145/316542.316548.&amp;lt;/ref&amp;gt; (хотя и с большей константой).&lt;br /&gt;
* &#039;&#039;&#039;Неотрицательные веса&#039;&#039;&#039;. Часто встречающееся на практике ограничение, когда вес ребра соответствует времени или стоимости прохождения участка маршрута и не может быть отрицательным.&lt;br /&gt;
* &#039;&#039;&#039;Произвольные веса&#039;&#039;&#039;. Наиболее общая постановка. Граф не должен содержать циклов с отрицательным суммарным весом, иначе кратчайшего пути не существует.&lt;br /&gt;
&lt;br /&gt;
== Свойства задачи ==&lt;br /&gt;
&lt;br /&gt;
Решение задачи удовлетворяет принципу оптимальности: если путь &amp;lt;math&amp;gt;P^*(u, w)&amp;lt;/math&amp;gt; является частью кратчайшего пути &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;P^*(u, w)&amp;lt;/math&amp;gt; является кратчайшим путём от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Принцип оптимальности означает, что для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; вместо всего кратчайшего пути &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt; достаточно хранить его последнее ребро &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt;. Отсюда следует оценка объёма памяти &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Описание входных и выходных данных ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Преобразование выходных данных и поиск кратчайшего пути ===&lt;br /&gt;
&lt;br /&gt;
Кратчайший путь от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; может быть восстановлен за время &amp;lt;math&amp;gt;O(\left |P^*(u, v) \right |)&amp;lt;/math&amp;gt;, если, начиная с вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, проходить рёбра &amp;lt;math&amp;gt;e^*_w&amp;lt;/math&amp;gt; в обратном направлении до тех пор, пока не будет посещена вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рёбра &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt; могут быть восстановлены за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; перебором рёбер для каждой вершины:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
    e^*_v \in \{ (w, v) \in E \mid f^*(v) = f^*(w) + f((v, w)) \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Перебор может осуществляться параллельно.&lt;br /&gt;
&lt;br /&gt;
Для поиска кратчайших расстояний &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; по набору рёбер &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt;, необходимо построить из них дерево, на котором выполнить [[Поиск в ширину (BFS)|поиск в ширину]] за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (с возможностью параллелизации).&lt;br /&gt;
&lt;br /&gt;
== Алгоритмы решения задачи ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[[Поиск в ширину (BFS)]]&#039;&#039;&#039; для ориентированных невзвешенных графов, сложность &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Дейкстры]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1, 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/5}}&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Беллмана-Форда]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; для ориентированных графов с произвольными весами, сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;. {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/6}}&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Δ-шагания]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Meyer, U, and P Sanders. “Δ-Stepping: a Parallelizable Shortest Path Algorithm.” Journal of Algorithms 49, no. 1 (October 2003): 114–52. doi:10.1016/S0196-6774(03)00076-2.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, средняя сложность на графах со случайными весами &amp;lt;math&amp;gt;O(n + m + dL)&amp;lt;/math&amp;gt;, параллельная сложность &amp;lt;math&amp;gt;O((dL + \ln n) \ln n&amp;lt;/math&amp;gt; со средней работой &amp;lt;math&amp;gt;O(n + m + dL \ln n)&amp;lt;/math&amp;gt;. {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/2}}&lt;br /&gt;
* &#039;&#039;&#039;Алгоритм Торупа&#039;&#039;&#039;&amp;lt;ref name=thorup /&amp;gt; представляет собой теоретическое доказательство возможности решения задачи для неориентированных графов с положительными целыми весами за линейное время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В ориентированном ациклическом графе с произвольными весами время поиска кратчайших путей линейное – &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;, алгоритм основан на топологической сортировке графа.&lt;br /&gt;
&lt;br /&gt;
Обозначения: &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; – число рёбер, &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; – число вершин, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; – максимальная степень вершины, &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; – максимальный суммарный вес кратчайшего пути.&lt;br /&gt;
&lt;br /&gt;
Перечисленные алгоритмы могут использоваться и для решения [[Поиск кратчайшего пути для всех пар вершин (APSP)|задачи поиска кратчайшего пути для всех пар вершин графа]] (для этого необходимо запустить соответствующий алгоритм для каждой вершины графа, что при необходимости можно сделать параллельно), при этом оценка сложности умножается на &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En: Single Source Shortest Path (SSSP)]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Buttonlink&amp;diff=25623</id>
		<title>Шаблон:Buttonlink</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Buttonlink&amp;diff=25623"/>
		<updated>2018-06-15T08:18:20Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[{{{1}}} &amp;lt;span style=&amp;quot;font-size:10px;&amp;quot; class=&amp;quot;mw-ui-button mw-ui-progressive&amp;quot;&amp;gt;Get perf. data&amp;lt;/span&amp;gt;]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=25621</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=25621"/>
		<updated>2018-06-14T14:12:27Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* Размещённый здесь код JavaScript будет загружаться пользователям при обращении к каждой странице */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
$(&#039;head&#039;).append(&amp;quot;&amp;lt;script type=&#039;text/javascript&#039; src=&#039;http://www.x3dom.org/download/x3dom.js&#039;&amp;gt; &amp;lt;/script&amp;gt; &amp;lt;link rel=&#039;stylesheet&#039; type=&#039;text/css&#039; href=&#039;http://www.x3dom.org/download/x3dom.css&#039;&amp;gt;&amp;lt;/link&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// $(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
$(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;1 0 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;transform translation=&#039;-3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 1 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;cone&amp;gt;&amp;lt;/cone&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;transform translation=&#039;3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 0 1&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;sphere&amp;gt;&amp;lt;/sphere&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var elem = $(&#039;div#mw-indicator-toplink&#039;).find(&#039;pre&#039;);&lt;br /&gt;
var cnt = elem.contents();&lt;br /&gt;
elem.replaceWith(cnt);&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=25620</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=25620"/>
		<updated>2018-06-14T14:09:08Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* Размещённый здесь код JavaScript будет загружаться пользователям при обращении к каждой странице */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
$(&#039;head&#039;).append(&amp;quot;&amp;lt;script type=&#039;text/javascript&#039; src=&#039;http://www.x3dom.org/download/x3dom.js&#039;&amp;gt; &amp;lt;/script&amp;gt; &amp;lt;link rel=&#039;stylesheet&#039; type=&#039;text/css&#039; href=&#039;http://www.x3dom.org/download/x3dom.css&#039;&amp;gt;&amp;lt;/link&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// $(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
$(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;1 0 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;transform translation=&#039;-3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 1 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;cone&amp;gt;&amp;lt;/cone&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;transform translation=&#039;3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 0 1&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;sphere&amp;gt;&amp;lt;/sphere&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
//var elem = $(&#039;div#mw-indicator-toplink&#039;).find(&#039;pre&#039;);&lt;br /&gt;
//var cnt = elem.contents();&lt;br /&gt;
//elem.replaceWith(cnt);&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=25619</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=MediaWiki:Common.js&amp;diff=25619"/>
		<updated>2018-06-14T14:07:34Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* Размещённый здесь код JavaScript будет загружаться пользователям при обращении к каждой странице */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
$(&#039;head&#039;).append(&amp;quot;&amp;lt;script type=&#039;text/javascript&#039; src=&#039;http://www.x3dom.org/download/x3dom.js&#039;&amp;gt; &amp;lt;/script&amp;gt; &amp;lt;link rel=&#039;stylesheet&#039; type=&#039;text/css&#039; href=&#039;http://www.x3dom.org/download/x3dom.css&#039;&amp;gt;&amp;lt;/link&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// $(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
$(&#039;#webgl-example&#039;).html(&amp;quot;&amp;lt;x3d width=&#039;600px&#039; height=&#039;400px&#039;&amp;gt;&amp;lt;scene&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;1 0 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;box&amp;gt;&amp;lt;/box&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;transform translation=&#039;-3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 1 0&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;cone&amp;gt;&amp;lt;/cone&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;transform translation=&#039;3 0 0&#039;&amp;gt;&amp;lt;shape&amp;gt;&amp;lt;appearance&amp;gt;&amp;lt;material diffuseColor=&#039;0 0 1&#039;&amp;gt;&amp;lt;/material&amp;gt;&amp;lt;/appearance&amp;gt;&amp;lt;sphere&amp;gt;&amp;lt;/sphere&amp;gt;&amp;lt;/shape&amp;gt;&amp;lt;/transform&amp;gt;&amp;lt;/scene&amp;gt;&amp;lt;/x3d&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var elem = $(&#039;div#mw-indicator-toplink&#039;).find(&#039;pre&#039;);&lt;br /&gt;
var cnt = elem.contents();&lt;br /&gt;
elem.replaceWith(cnt);&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%BE%D1%82_%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B_(SSSP)&amp;diff=25618</id>
		<title>Поиск кратчайшего пути от одной вершины (SSSP)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%BE%D1%82_%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B_(SSSP)&amp;diff=25618"/>
		<updated>2018-06-14T13:22:52Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-p}}&lt;br /&gt;
&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/5}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
== Постановка задачи ==&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Последовательность &amp;lt;math&amp;gt;P(u, v)&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_1 = (u, w_1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e_2 = (w_1, w_2)&amp;lt;/math&amp;gt;, …, &amp;lt;math&amp;gt;e_k = (w_{k-1}, v)&amp;lt;/math&amp;gt; называется &#039;&#039;путём&#039;&#039;, идущим от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к вершине &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Суммарный вес этого пути равен&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f(P(u, v)) = \sum_{i = 1}^k f(e_i).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Требуется для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, достижимой из вершины-источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, указать путь &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt;, имеющий наименьший возможный суммарный вес:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f^*(v) = f(P^*(u, v)) = \min f(P(u, v)).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Название задачи на английском языке – «Single Source Shortest Path» (SSSP).&lt;br /&gt;
&lt;br /&gt;
== Варианты задачи ==&lt;br /&gt;
&lt;br /&gt;
Если требуется найти кратчайшие пути не от одной, а от всех вершин - см. [[Поиск кратчайшего пути для всех пар вершин (APSP)|поиск кратчайшего пути для всех пар вершин]].&lt;br /&gt;
&lt;br /&gt;
Задача может быть поставлена как для ориентированного, так и для неориентированного графа. Приведённая постановка задачи применима в обоих случаях.&lt;br /&gt;
&lt;br /&gt;
В зависимости от ограничений на возможные значения весов различают следующие случаи.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Единичные веса&#039;&#039;&#039;. В этом случае задача существенно упрощается и может быть решена за линейное время [[Поиск в ширину (BFS)|алгоритмом поиска вширь]].&lt;br /&gt;
* &#039;&#039;&#039;Положительные целые веса&#039;&#039;&#039;. Задача также может быть решена за линейное время&amp;lt;ref name=thorup&amp;gt;Thorup, Mikkel. “Undirected Single-Source Shortest Paths with Positive Integer Weights in Linear Time.” Journal of the ACM 46, no. 3 (May 1, 1999): 362–94. doi:10.1145/316542.316548.&amp;lt;/ref&amp;gt; (хотя и с большей константой).&lt;br /&gt;
* &#039;&#039;&#039;Неотрицательные веса&#039;&#039;&#039;. Часто встречающееся на практике ограничение, когда вес ребра соответствует времени или стоимости прохождения участка маршрута и не может быть отрицательным.&lt;br /&gt;
* &#039;&#039;&#039;Произвольные веса&#039;&#039;&#039;. Наиболее общая постановка. Граф не должен содержать циклов с отрицательным суммарным весом, иначе кратчайшего пути не существует.&lt;br /&gt;
&lt;br /&gt;
== Свойства задачи ==&lt;br /&gt;
&lt;br /&gt;
Решение задачи удовлетворяет принципу оптимальности: если путь &amp;lt;math&amp;gt;P^*(u, w)&amp;lt;/math&amp;gt; является частью кратчайшего пути &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;P^*(u, w)&amp;lt;/math&amp;gt; является кратчайшим путём от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Принцип оптимальности означает, что для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; вместо всего кратчайшего пути &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt; достаточно хранить его последнее ребро &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt;. Отсюда следует оценка объёма памяти &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Описание входных и выходных данных ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Преобразование выходных данных и поиск кратчайшего пути ===&lt;br /&gt;
&lt;br /&gt;
Кратчайший путь от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; может быть восстановлен за время &amp;lt;math&amp;gt;O(\left |P^*(u, v) \right |)&amp;lt;/math&amp;gt;, если, начиная с вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, проходить рёбра &amp;lt;math&amp;gt;e^*_w&amp;lt;/math&amp;gt; в обратном направлении до тех пор, пока не будет посещена вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рёбра &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt; могут быть восстановлены за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; перебором рёбер для каждой вершины:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
    e^*_v \in \{ (w, v) \in E \mid f^*(v) = f^*(w) + f((v, w)) \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Перебор может осуществляться параллельно.&lt;br /&gt;
&lt;br /&gt;
Для поиска кратчайших расстояний &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; по набору рёбер &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt;, необходимо построить из них дерево, на котором выполнить [[Поиск в ширину (BFS)|поиск в ширину]] за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (с возможностью параллелизации).&lt;br /&gt;
&lt;br /&gt;
== Алгоритмы решения задачи ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[[Поиск в ширину (BFS)]]&#039;&#039;&#039; для ориентированных невзвешенных графов, сложность &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Дейкстры]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1, 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/5}}&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Беллмана-Форда]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; для ориентированных графов с произвольными весами, сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;. {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/6}}&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Δ-шагания]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Meyer, U, and P Sanders. “Δ-Stepping: a Parallelizable Shortest Path Algorithm.” Journal of Algorithms 49, no. 1 (October 2003): 114–52. doi:10.1016/S0196-6774(03)00076-2.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, средняя сложность на графах со случайными весами &amp;lt;math&amp;gt;O(n + m + dL)&amp;lt;/math&amp;gt;, параллельная сложность &amp;lt;math&amp;gt;O((dL + \ln n) \ln n&amp;lt;/math&amp;gt; со средней работой &amp;lt;math&amp;gt;O(n + m + dL \ln n)&amp;lt;/math&amp;gt;. {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/2}}&lt;br /&gt;
* &#039;&#039;&#039;Алгоритм Торупа&#039;&#039;&#039;&amp;lt;ref name=thorup /&amp;gt; представляет собой теоретическое доказательство возможности решения задачи для неориентированных графов с положительными целыми весами за линейное время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В ориентированном ациклическом графе с произвольными весами время поиска кратчайших путей линейное – &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;, алгоритм основан на топологической сортировке графа.&lt;br /&gt;
&lt;br /&gt;
Обозначения: &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; – число рёбер, &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; – число вершин, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; – максимальная степень вершины, &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; – максимальный суммарный вес кратчайшего пути.&lt;br /&gt;
&lt;br /&gt;
Перечисленные алгоритмы могут использоваться и для решения [[Поиск кратчайшего пути для всех пар вершин (APSP)|задачи поиска кратчайшего пути для всех пар вершин графа]] (для этого необходимо запустить соответствующий алгоритм для каждой вершины графа, что при необходимости можно сделать параллельно), при этом оценка сложности умножается на &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En: Single Source Shortest Path (SSSP)]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=25617</id>
		<title>Алгоритм Дейкстры</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=25617"/>
		<updated>2018-06-14T13:18:30Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-a}}&lt;br /&gt;
&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/5}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
Основные авторы описания: [[Участник:Daryin|А.Н.Дарьин]], [[Участник:VadimVV|Вад.В.Воеводин]] ([[#Описание локальности данных и вычислений|раздел 2.2]])&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Дейкстры&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа.&lt;br /&gt;
Алгоритм Дейкстры (с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt;) выполняется за время &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt; и является асимптотически быстрейшим из известных последовательных алгоритмов для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть уже вычислены все расстояния, не превосходящие некоторого числа &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;, то есть расстояния до вершин из множества &amp;lt;math&amp;gt;V_r = \{ v \in V \mid d(v) \le r \}&amp;lt;/math&amp;gt;. Пусть&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        (v, w) \in \arg\min \{ d(v) + f(e) \mid  v \in V, e = (v, w) \in E \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Тогда &amp;lt;math&amp;gt;d(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; лежит на кратчайшем пути от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Величины &amp;lt;math&amp;gt;d^+(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v \in V_r&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e = (v, w) \in E&amp;lt;/math&amp;gt;, называются &#039;&#039;предполагаемыми расстояниями&#039;&#039; и являются оценкой сверху для настоящих расстояний: &amp;lt;math&amp;gt;d(w) \le d^+(w)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры на каждом шаге находит вершину с наименьшим предполагаемым расстоянием, помечает её как посещённую и обновляет предполагаемые расстояния для всех концов рёбер, исходящих из неё.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основные вычисления связаны с операциями над очередью с приоритетом:&lt;br /&gt;
* извлечение минимального элемента (&amp;lt;code&amp;gt;delete_min&amp;lt;/code&amp;gt;);&lt;br /&gt;
* уменьшение приоритета элемента (&amp;lt;code&amp;gt;decrease_key&amp;lt;/code&amp;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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;Q&#039;&#039; := &#039;&#039;&#039;new&#039;&#039;&#039; priority queue&lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;:&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;v&#039;&#039; = &#039;&#039;u&#039;&#039; &#039;&#039;&#039;then&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := 0 &#039;&#039;&#039;else&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞ &lt;br /&gt;
     &#039;&#039;Q&#039;&#039;.insert(&#039;&#039;v&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;))&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;while&#039;&#039;&#039; &#039;&#039;Q&#039;&#039; ≠ ∅:&lt;br /&gt;
     &#039;&#039;v&#039;&#039; := &#039;&#039;Q&#039;&#039;.delete_min()&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
             &#039;&#039;Q&#039;&#039;.decrease_key(&#039;&#039;w&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;))&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Конкретная реализация алгоритма Дейкстры определяется выбором используемого алгоритма очереди с приоритетом. В простейшем случае это может быть массив или список, поиск минимума в котором требует просмотра всех вершин. Более эффективным является использование кучи; наилучшую известную оценку сложности имеет вариант с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Возможен вариант реализации, когда вершины добавляются в очередь не на этапе инициализации, а в момент первого посещения.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательная сложность алгоритма равна &amp;lt;math&amp;gt;O(C_1 m + C_2n)&amp;lt;/math&amp;gt;, где&lt;br /&gt;
* &amp;lt;math&amp;gt;C_1&amp;lt;/math&amp;gt; – количество операций уменьшения расстояния до вершины;&lt;br /&gt;
* &amp;lt;math&amp;gt;C_2&amp;lt;/math&amp;gt; – количество операций вычисления минимума.&lt;br /&gt;
&lt;br /&gt;
Оригинальный алгоритм Дейкстры использовал в качестве внутренней структуры данных списки, для которых &amp;lt;math&amp;gt;C_1 = O(1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;C_2 = O(n)&amp;lt;/math&amp;gt;, так что общая сложность составляла &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При использовании фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt; время вычисления минимума сокращается до &amp;lt;math&amp;gt;C_2 = O(\ln n)&amp;lt;/math&amp;gt;, так что общая сложность равна &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;, что является асимптотически наилучшим известным результатом для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
Приводится граф алгоритма для базовой реализации алгоритма Дейкстры на списках или массивах.&lt;br /&gt;
[[file:Deikstra.png|thumb|center|600px|Рисунок 1. Граф алгоритма без отображения входных и выходных данных. n=3. Желтым цветом обозначены операции сравнения, зеленым - операции изменения меток вершин, синим - пометка вершины.]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры допускает эффективную параллелизацию&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;, среднее время работы &amp;lt;math&amp;gt;O(n^{1/3}\ln n)&amp;lt;/math&amp;gt; с объёмом вычислений &amp;lt;math&amp;gt;O(n \ln n + m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Дейкстры.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(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;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_1.png|thumb|center|700px|Рисунок 1. Реализация алгоритма Дейкстры. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 1 представлен профиль обращений в память для реализации алгоритма Дейкстры. Первое, что бросается в глаза, – большая разрозненность обращений. В частности, значительные области выше и ниже фрагмента 2 остаются пустыми, при этом сами обращения объединены лишь в небольшие группы. Это говорит о низкой эффективности, поскольку: а) повторные обращения практически отсутствуют либо происходят через значительный промежуток времени; б) расстояния между идущими подряд обращениями может быть очень большим.&lt;br /&gt;
&lt;br /&gt;
Однако при ближайшем рассмотрении может оказаться, что такие участки обладают высокой локальностью и состоят из значительного числа обращений. Более того, на общем профиле есть несколько областей (фрагменты 1 и 2), в которых обращения хорошо локализованы. Необходимо исследовать отдельные участки более подробно.&lt;br /&gt;
&lt;br /&gt;
Перейдем к изучению фрагмента 1 (рис. 2), в рамках которого выполняются обращения к двум небольшим массивам. Можно увидеть, что здесь задействованы только примерно 500 элементов, при этом к ним выполняется около 100 тысяч обращений. Весь профиль составляет около 120 тысяч обращений, поэтому получается, что подавляющая их часть выполняется именно к этим элементам. &lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_2.png|thumb|center|700px|Рисунок 2. Профиль обращений, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Поскольку число элементов невелико, локальность в данном случае заведомо будет достаточно высокой, независимо от структуры самого фрагмента.  Однако на рис. 3, где представлены два участка из фрагмента 1, можно увидеть, что фрагмент в основном состоит из последовательных переборов, при этом данные зачастую задействованы повторно через не очень большие промежутки. Все это позволяет говорить, что данный фрагмент обладает высокой как пространственной, так и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_3.png|thumb|center|700px|Рисунок 3. Профили двух участков фрагмента 1 (выделены зеленым на рис. 2)]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим теперь более подробно фрагмент 2 (рис. 4), в рамках которого выполняются обращения к еще служебному массиву. Здесь профиль состоит из двух этапов. На первом заметен достаточно хаотичный разброс обращений, напоминающий случайный доступ. На втором обращения образуют подобие последовательного перебора.  В целом такой профиль характеризуется очень низкой временной локальностью (повторные обращения практически или полностью отсутствуют) и достаточно низкой пространственной локальностью (из-за случайного доступа на первом этапе).&lt;br /&gt;
&lt;br /&gt;
Заметим, что при этом число задействованных элементов здесь больше, чем во фрагменте 1, однако число обращений гораздо меньше.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_4.png|thumb|center|500px|Рисунок 4. Профиль обращений, фрагмент 2]]&lt;br /&gt;
&lt;br /&gt;
Далее остаются для рассмотрения два массива (область между фрагментами 1 и 2 и область ниже фрагмента 2). Характер обращений к этим массивам во многом похож, поэтому достаточно изучить более подробно только один из них. &lt;br /&gt;
&lt;br /&gt;
Фрагмент 3 рассмотрен на рис. 5. Этот участок отражаeт достаточно большую область, что не позволяет проанализировать профиль вплоть до отдельных обращений, однако здесь этого и не требуется. Из данного фрагмента видно, что основу профиля составляют участки с последовательным (или с небольшим шагом) перебором, состоящие из небольшого числа элементов – выделенный желтым самый большой участок фрагмента состоит всего из пары сотен обращений. При этом между разными участками расстояние может быть существенным. Все это говорит об очень низкой локальности (как пространственной, так и временной) в случае двух рассматриваемых массивов.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_5.png|thumb|center|500px|Рисунок 5. Профиль обращений, фрагмент 3]]&lt;br /&gt;
&lt;br /&gt;
В целом, несмотря на положительный вклад массивов из фрагмента 1, локальность общего профиля должна быть достаточно низкой, вследствие неэффективного использования данных в остальной части профиля.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Основной фрагмент реализации, на основе которого были получены количественные оценки, приведен [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/benchmarks/dijkstra/dijkstra.h здесь] (функция Kernel). Условия запуска описаны [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/README.md здесь].&lt;br /&gt;
&lt;br /&gt;
Первая оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью достаточно низка. Это неудивительно, поскольку реализации алгоритмов над графами почти всегда обладают низкой эффективностью вследствие нерегулярности доступа к данным, что мы и увидели при анализе профиля обращений.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
Вторая характеристика – cvg – предназначена для получения более машинно-независимой оценки локальности. Она определяет, насколько часто в программе необходимо подтягивать данные в кэш-память. Соответственно, чем меньше значение cvg, тем реже это нужно делать, тем лучше локальность. &lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения cvg для того же набора реализаций, отсортированные по убыванию (чем меньше cvg, тем в общем случае выше локальность). Можно увидеть, что в данном случае значение cvg хорошо коррелирует с оценкой производительности и отражает низкую локальность, что соответствует выводам, сделанным при качественной оценке локальности.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_cvg.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки cvg]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot;&amp;lt;ref name=&amp;quot;Lom&amp;quot;&amp;gt;Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&amp;lt;/ref&amp;gt; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [4, 8 : 128] со значениями квадрата целого числа;&lt;br /&gt;
*	размер графа [16000:64000] с шагом 16000.&lt;br /&gt;
&lt;br /&gt;
На следующем рисунке приведен график [[Глоссарий#Производительность|производительности]] выбранной реализации алгоритма в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra perf.png|thumb|center|700px|Рисунок 8. Параллельная реализация алгоритма. Изменение производительности в зависимости от числа процессоров и размера области.]]&lt;br /&gt;
&lt;br /&gt;
В силу особенностей параллельной реализации алгоритма производительность в целом достаточно низкая и с ростом числа процессов увеличивается медленно, а при приближении к числу процессов 128 начинает уменьшаться.&lt;br /&gt;
Это объясняется использованием коллективных операций на каждой итерации алгоритма и тем, что затраты на коммуникационные обмены существенно возрастают с ростом числа использованных процессов. Вычисления на каждом процессе проходят достаточно быстро и потому декомпозиция графа слабо компенсирует эффект от затрат на коммуникационные обмены. &lt;br /&gt;
&lt;br /&gt;
[https://code.google.com/p/prir-dijkstra-mpi/source/browse/trunk/project/dijkstra_mpi.c Исследованная параллельная реализация на языке C]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация алгоритма Дейкстры.  Все результаты получены на суперкомпьютере &amp;quot;Ломоносов&amp;quot;. Использовались процессоры Intel Xeon X5570 с пиковой производительностью в 94 Гфлопс, а также компилятор intel 13.1.0. &lt;br /&gt;
На рисунках показана эффективность реализации алгоритма Дейкстры  на 32 процессах.&lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra CPU User.png|thumb|center|700px|Рисунок 9. График загрузки CPU при выполнении алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы уровень загрузки составляет около 50%. Это указывает на равномерную загруженность вычислениями процессоров, при использовании 8 процессов на вычислительный узел и без использования Hyper Threading. &lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra Flops.png|thumb|center|700px|Рисунок 10. График операций с плавающей точкой в секунду при выполнении алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На Рисунке 10 показан график количества операций с плавающей точкой в секунду. На графике видна общая очень низкая производительность вычислений около 250 Kфлопс в пике и около 150 Кфлопс в среднем по всем узлам. Это указывает то, что в программе почти все вычисления производятся с целыми числами. &lt;br /&gt;
 [[file:Dijkstra L1.png|thumb|center|700px|Рисунок 11. График кэш-промахов L1 в секунду при работе алгоритма Дейкстры]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов очень большое для нескольких ядер и находится на уровне 15 млн/сек (в пике до 60 млн/сек), что указывает на интенсивные вычисления в части процессов. В среднем по всем узлам значения значительно ниже (около 9 млн/сек). Это указывает на неравномерное распределение вычислений.&lt;br /&gt;
 [[file:Dijkstra L3.png|thumb|center|700px|Рисунок 12. График кэш-промахов L3 в секунду при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов третьего уровня видно, что число промахов очень невелико и находится на уровне 1,2 млн/сек, однако в среднем по всем узлам значения около 0,5 млн/сек. Соотношение кэш промахов L1|L3 для процессов с высокой производительностью доходит до 60, однако в среднем около 30. Это указывает на очень хорошую локальность вычислений как у части процессов, так и для всех в среднем, и это является признаком высокой производительности. &lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra Mem Load.png|thumb|center|700px|Рисунок 13. График количества чтений из оперативной памяти при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике обращений в память видна достаточно типичная картина для таких приложений. Активность чтения достаточно низкая, что в совокупности с низкими значениями кэш-промахов L3 указывает на хорошую локальность. Хорошая локальность приложения также указывает на то, что значение около 1 млн/сек для задачи является результатом высокой производительности вычислений, хотя и присутствует неравномерность между процессами.&lt;br /&gt;
 [[file:Dijkstra Mem Store.png|thumb|center|700px|Рисунок 14. График количества записей в оперативную память при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике записей в память видна похожая картина неравномерности вычислений, при которой одновременно активно выполняют запись только несколько процессов. Это коррелирует с другими графиками выполнения. Стоит отметить достаточно низкое число обращений на запись в память. Это указывает на хорошую организацию вычислений и достаточно эффективную работу с памятью.&lt;br /&gt;
 [[file:Dijkstra Inf DATA.png|thumb|center|700px|Рисунок 15. График скорости передачи по сети Infiniband в байт/сек при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно высокая скорость передачи данных в байтах в секунду. Это говорит о том, что процессы между собой обмениваются интенсивно и вероятно достаточно малыми порциями данных, потому как производительность вычислений высока. Стоит отметить, что скорость передачи отличается между процессами, что указывает на дисбаланс вычислений.&lt;br /&gt;
 [[file:Dijkstra Inf PCKTS.png|thumb|center|700px|Рисунок 16. График скорости передачи по сети Infiniband в пакетах/сек при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается крайне высокая интенсивность передачи данных. Это говорит о том, что, вероятно, процессы обмениваются не очень существенными объемами данных, но очень интенсивно. Используются коллективные операции на каждом шаге с небольшими порциями данных, что объясняет такую картину.  Также наблюдается меньший дизбаланс между процессами, чем наблюдаемый в графиках использования памяти, вычислений и передачи данных в байтах/сек. Это указывает на то, что процессы обмениваются по алгоритму одинаковым числом пакетов, однако получают разные объемы данных и ведут неравномерные вычисления. &lt;br /&gt;
 [[file:Dijkstra Load ABG.png|thumb|center|700px|Рисунок 17. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе алгоритма Дейкстры]]&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно и приблизительно равняется 8. Это свидетельствует о стабильной работе программы с загруженными вычислениями всеми узлами. Это указывает на очень рациональную и статичную загрузку аппаратных ресурсов процессами. И показывает достаточно хорошую эффективность выполняемой реализации.&lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала достаточно эффективно, и стабильно. Использование памяти  очень интенсивное, а использование коммуникационной среды крайне интенсивное, при этом объемы передаваемых данных не являются высокими. Это указывает на требовательность к латентности коммуникационной среды алгоритмической части программы. &lt;br /&gt;
Низкая эффективность связана, судя по всему, с достаточно высоким объемом пересылок на каждом процессе и с интенсивными обменами сообщениями.&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths.html dijkstra_shortest_paths]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths_no_color_map.html dijkstra_shortest_paths_no_color_map]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. &lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]:&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#eager-dijkstra-s-algorithm eager_dijkstra_shortest_paths]&amp;lt;/code&amp;gt; – непосредственная реализация алгоритма Дейкстры;&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#crauser-et-al-s-algorithm crauser_et_al_shortest_paths]&amp;lt;/code&amp;gt; – реализация алгоритма Дейкстры в виде алгоритма из статьи Краузера и др.&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/14}}&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.single_source_dijkstra.html single_source_dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python/C++: [https://networkit.iti.kit.edu NetworKit] (класс &amp;lt;code&amp;gt;[https://networkit.iti.kit.edu/data/uploads/docs/NetworKit-Doc/python/html/graph.html#networkit.graph.Dijkstra networkit.graph.Dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En:Dijkstra&#039;s algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=25616</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=25616"/>
		<updated>2018-06-14T13:14:59Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &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(|V||E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;N/A, max O(|V|) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(|V| + |E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(|V|^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/6}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(|V||E|)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(|E| + |V|\ln(|V|))&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В самом начале производится присваивание &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;d(v) = \infty&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\forall v \ne u&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Далее происходит &amp;lt;math&amp;gt;|V|-1&amp;lt;/math&amp;gt; итерация, в ходе каждой из которых производится релаксация всех рёбер графа.&lt;br /&gt;
&lt;br /&gt;
Структуру можно описать следующим образом:&lt;br /&gt;
&lt;br /&gt;
1.	Инициализация: всем вершинам присваивается предполагаемое расстояние &amp;lt;math&amp;gt;t(v)=\infty&amp;lt;/math&amp;gt;, кроме вершины-источника, для которой &amp;lt;math&amp;gt;t(u)=0&amp;lt;/math&amp;gt; .&lt;br /&gt;
&lt;br /&gt;
2.	Релаксация множества рёбер &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
а)	Для каждого ребра &amp;lt;math&amp;gt;e=(v,z) \in E&amp;lt;/math&amp;gt; вычисляется новое предполагаемое расстояние &amp;lt;math&amp;gt;t^&#039; (z)=t(v)+ w(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
б)	Если &amp;lt;math&amp;gt;t^&#039; (z)&amp;lt; t(z)&amp;lt;/math&amp;gt;, то происходит присваивание &amp;lt;math&amp;gt;t(z) := t&#039; (z)&amp;lt;/math&amp;gt; (релаксация ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; ).&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм производит релаксацию всех рёбер графа до тех пор, пока на очередной итерации происходит релаксация хотя бы одного ребра.&lt;br /&gt;
&lt;br /&gt;
Если на &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt;-й итерации всё ещё производилась релаксацию рёбер, то в графе присутствует цикл отрицательной длины. Ребро &amp;lt;math&amp;gt;e=(v,z)&amp;lt;/math&amp;gt;, лежащее на таком цикле, может быть найдено проверкой следующего условия (проверяется для всех рёбер за линейное время): &amp;lt;math&amp;gt;t(v)+w(e)&amp;lt;d(z)&amp;lt;/math&amp;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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
 &#039;&#039;d&#039;&#039;(&#039;&#039;u&#039;&#039;) = 0&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;i&#039;&#039; &#039;&#039;&#039;from&#039;&#039;&#039; 1 &#039;&#039;&#039;to&#039;&#039;&#039; |&#039;&#039;V&#039;&#039;| - 1:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;w&#039;&#039;, &#039;&#039;v&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Алгоритм выполняет &amp;lt;math&amp;gt;|V|-1&amp;lt;/math&amp;gt; итерацию, на каждой из которых происходит релаксация &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбер. Таким образом, общий объём работы составляет &amp;lt;math&amp;gt;O(|V||E|)&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 представлен информационный граф алгоритма, демонстрирующий описанные уровни параллелизма.&lt;br /&gt;
&lt;br /&gt;
[[file:APSP.png|thumb|center|700px|Рисунок 1. Информационный граф обобщенного алгоритма Беллмана-Форда.]]&lt;br /&gt;
&lt;br /&gt;
На приведенном далее информационном графе нижний уровень параллелизма обозначен в горизонтальных плоскостях. Множество всех плоскостей представляет собой верхний уровень параллелизма (операции в каждой плоскости могут выполняться параллельно).&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма расположен на уровнях [2] и [3], соответствующим операциям инициализации массива дистанций [2] и обновления массива c использованием данных массива ребер [3]. Операция [4] - проверка того, были ли изменения на последней итерации и выход из цикла, если таковых не было.&lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма, как уже говорилось, заключается в параллельном подсчете дистанций для различных вершин-источников, и на рисунке отмечен разными плоскостями.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм обладает значительным ресурсом параллелизма. Во-первых, поиск кратчайших путей от различных вершин может производиться независимо для каждой из вершин (параллельные вертикальные плоскости на рисунке 1). Во-вторых, поиск кратчайших путей от фиксированной вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; также может выполняться параллельно: инициализация начальных путей [2] требует &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; параллельных операции, релаксация каждого ребра требует &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt; параллельных операции. &lt;br /&gt;
&lt;br /&gt;
Таким образом, при наличии &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt; процессоров алгоритм завершит работу максимум за &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; шагов. В реальности, шагов обычно требуется меньше, а именно &amp;lt;math&amp;gt;O(r)&amp;lt;/math&amp;gt; -(максимальная длина среди всех кратчайших путей от выбранной вершины-источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Таким образом, ширина ярусно-параллельной формы алгоритма равна &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;, высота ЯПФ - &amp;lt;math&amp;gt;O(r) | r &amp;lt; |V|&amp;lt;/math&amp;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;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V| + |E|)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Алгоритм может распознавать наличие отрицательных циклов в графе. Ребро &amp;lt;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(|E|)&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;
[[file:bellman_ford_1.png|thumb|center|700px|Рисунок 2. Алгоритм Беллмана-Форда. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации алгоритма Беллмана-Форда. Первое, что сразу стоит отметить – число обращений в память гораздо больше числа задействованных данных. Это говорит о частом повторном использовании одних и тех же данных, что обычно приводит к высокой временной локальности. Далее, видна явная регулярная структура производимых обращений в память, видны повторяющиеся итерации работы алгоритма. Практически все обращения образуют фрагменты, похожие на последовательный перебор, кроме самой верхней части, где наблюдается более сложная структура.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим более детально отдельные фрагменты общего профиля, чтобы лучше разобраться в его структуре. На рис. 3 представлен фрагмент 1 (выделен на рис. 2), на котором показаны первые 500 обращений в память (отметим, что другой наклон двух последовательных переборов связан с измененным отношением сторон в рассматриваемой области). Данный рисунок показывает, что выделенные желтым части 1 и 2 являются практически идентичными последовательными переборами; отличие между ними только в том, что в части 1 обращения выполняются в два раза чаще, поэтому на рис. 3 эта часть представлена большим числом точек. Как мы знаем, подобные профили характеризуются высокой пространственной и низкой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим более интересный фрагмент 2, отмеченный на рис. 2 (см. рис. 4). Здесь можно снова увидеть подтверждение регулярности обращений в нижней области профиля, однако верхняя область явно устроена гораздо сложнее; хотя и здесь просматривается регулярность. В частности, также видны те же самые итерации, в которых здесь можно выделить большие последовательности обращений к одним и тем же данным. Пример такого поведения, оптимального с точки зрения локальности, выделен на рисунке желтым. &lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_3.png|thumb|center|700px|Рисунок 4. Профиль обращений, фрагмент 2]]&lt;br /&gt;
&lt;br /&gt;
Чтобы понять структуру обращений в память в верхней части, можно рассмотреть ее еще подробнее. Приведем визуализацию небольшой области фрагмента 2, выделенной на рис. 4 зеленым. Однако в данном случае дальнейшее приближение (рис. 5) не привносит большей ясности: видна нерегулярная структура внутри итерации, характер которой достаточно сложно описать. Но в данном случае этого и не требуется – можно заметить, что по вертикали отложено всего 15 элементов, при этом обращений к ним выполняется гораздо больше. Независимо от структуры обращений, такой профиль обладает очень высокой как пространственной, так и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_4.png|thumb|center|700px|Рисунок 5. Небольшая часть фрагмента 2]]&lt;br /&gt;
&lt;br /&gt;
А так как основная масса обращений приходится именно на фрагмент 2, можно утверждать, что и весь общий профиль обладает высокой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что по производительности работы с памятью данная реализация алгоритма показывает очень хорошие результаты. В частности, значение daps сравнимо с оценкой для теста Linpack, который известен высокой эффективностью взаимодействия с подсистемой памяти. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая алгоритм поиска кратчайших путей, состоит из двух частей: части, отвечающей за общую координацию вычислений, а также параллельные вычисления на многоядерных CPU, и GPU части, отвечающей только за вычисления на графическом ускорителе.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Алгоритм обладает значительным потенциалом масштабируемости, так как каждое ребро обрабатывается независимо и можно поручить каждому вычислительному процессу свою часть рёбер графа. Узким местом является доступ к разделяемому всеми процессами массиву расстояний. Алгоритм позволяет ослабить требования к синхронизации данных этого массива между процессами (когда один процесс может не сразу увидеть новое значение расстояния, записанное другим процессом), за счёт, может быть, большего количества глобальных итераций.&lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Беллмана-Форда согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости вширь реализации алгоритма Беллмана-Форда.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера.&lt;br /&gt;
&lt;br /&gt;
[[file:Сильная_масштабируемость_вширь_алгоритма_Беллмана-Форда.png|thumb|center|700px|Рисунок 7. Параллельная реализация алгоритма Беллмана-Форда масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация алгоритма Беллмана-Форда, реализованная для CPU.  Все результаты получены на суперкомпьютере «Ломоносов-2». Использовались процессоры Intel Xeon E5-2697v3, задача решалась для графа большого размера на одном узле. &lt;br /&gt;
На рисунках показана эффективность реализации алгоритма Беллмана-Форда, запуск проводился на 1 узле для графа 2^27, выполнялась 1 итерация.&lt;br /&gt;
[[file:Bellman-Ford cpu.png|thumb|center|700px|Рисунок 8. График загрузки CPU при выполнении алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы не загружены и средний уровень загрузки составляет около 5%. Это достаточно неэффективная картина даже для программ, запущенных c использованием одного ядра в реализации. &lt;br /&gt;
&lt;br /&gt;
[[file:Bellman-Ford LoadAvg.png|thumb|center|700px|Рисунок 9. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно на уровне 1. Это указывает на постоянную загрузку аппаратных ресурсов не более чем 1 процессом, однако это число для узла слишком мало, что может указывать на не очень рациональное использование ресурсов.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford L1.png|thumb|center|700px|Рисунок 10. График кэш-промахов L1 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов очень высокое и находится на уровне 140 млн/сек. Это очень высокий показатель, показывающий потенциальную причину неэффективности.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford L2.png|thumb|center|700px|Рисунок 11. График кэш-промахов L2 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов второго уровня видно, что число таких промахов тоже крайне высокое и находится на уровне 140 млн/сек, что указывает на крайне неэффективную работу с памятью.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford-L3.png|thumb|center|700px|Рисунок 12. График кэш-промахов L3 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов последнего уровня видно, что число промахов тоже достаточно большое и составляет около 30 млн/сек по всем узлам. Это указывает на то, что задача очень плохо укладывается в кэш-память, и программа постоянно работает с оперативной памятью, что объясняется очень большим размером использованного графа.&lt;br /&gt;
&lt;br /&gt;
[[file:Bellman-Ford Inf Data.png|thumb|center|700px|Рисунок 13. График скорости передачи по сети Infiniband в байт/сек при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно высокая интенсивность использования сети на первом этапе. Это объясняется программной логикой, которая предполагает чтение графа из файла с диска, коммуникации с которым происходят на Ломоносов-2 через выделенную для этих задач сеть Infiniband.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford Inf Pckts.png|thumb|center|700px|Рисунок 14. График скорости передачи по сети Infiniband в пакетах/сек при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается аналогичная картина очень высокой интенсивности на первом этапе выполнения задачи. Далее сеть почти не используется.&lt;br /&gt;
 &lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала стабильно интенсивно,  однако очень неэффективно использовала память из-за очень большого размера графа.&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En:Bellman-Ford algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=25615</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=25615"/>
		<updated>2018-06-14T13:14:26Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &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(|V||E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;N/A, max O(|V|) &amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(|V| + |E|)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(|V|^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/5}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
http://top53.parallel.ru/algo_results/algorithm/6&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(|V||E|)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(|E| + |V|\ln(|V|))&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В самом начале производится присваивание &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;d(v) = \infty&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\forall v \ne u&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Далее происходит &amp;lt;math&amp;gt;|V|-1&amp;lt;/math&amp;gt; итерация, в ходе каждой из которых производится релаксация всех рёбер графа.&lt;br /&gt;
&lt;br /&gt;
Структуру можно описать следующим образом:&lt;br /&gt;
&lt;br /&gt;
1.	Инициализация: всем вершинам присваивается предполагаемое расстояние &amp;lt;math&amp;gt;t(v)=\infty&amp;lt;/math&amp;gt;, кроме вершины-источника, для которой &amp;lt;math&amp;gt;t(u)=0&amp;lt;/math&amp;gt; .&lt;br /&gt;
&lt;br /&gt;
2.	Релаксация множества рёбер &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
а)	Для каждого ребра &amp;lt;math&amp;gt;e=(v,z) \in E&amp;lt;/math&amp;gt; вычисляется новое предполагаемое расстояние &amp;lt;math&amp;gt;t^&#039; (z)=t(v)+ w(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
б)	Если &amp;lt;math&amp;gt;t^&#039; (z)&amp;lt; t(z)&amp;lt;/math&amp;gt;, то происходит присваивание &amp;lt;math&amp;gt;t(z) := t&#039; (z)&amp;lt;/math&amp;gt; (релаксация ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; ).&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм производит релаксацию всех рёбер графа до тех пор, пока на очередной итерации происходит релаксация хотя бы одного ребра.&lt;br /&gt;
&lt;br /&gt;
Если на &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt;-й итерации всё ещё производилась релаксацию рёбер, то в графе присутствует цикл отрицательной длины. Ребро &amp;lt;math&amp;gt;e=(v,z)&amp;lt;/math&amp;gt;, лежащее на таком цикле, может быть найдено проверкой следующего условия (проверяется для всех рёбер за линейное время): &amp;lt;math&amp;gt;t(v)+w(e)&amp;lt;d(z)&amp;lt;/math&amp;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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
 &#039;&#039;d&#039;&#039;(&#039;&#039;u&#039;&#039;) = 0&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;i&#039;&#039; &#039;&#039;&#039;from&#039;&#039;&#039; 1 &#039;&#039;&#039;to&#039;&#039;&#039; |&#039;&#039;V&#039;&#039;| - 1:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;w&#039;&#039;, &#039;&#039;v&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Алгоритм выполняет &amp;lt;math&amp;gt;|V|-1&amp;lt;/math&amp;gt; итерацию, на каждой из которых происходит релаксация &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбер. Таким образом, общий объём работы составляет &amp;lt;math&amp;gt;O(|V||E|)&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 представлен информационный граф алгоритма, демонстрирующий описанные уровни параллелизма.&lt;br /&gt;
&lt;br /&gt;
[[file:APSP.png|thumb|center|700px|Рисунок 1. Информационный граф обобщенного алгоритма Беллмана-Форда.]]&lt;br /&gt;
&lt;br /&gt;
На приведенном далее информационном графе нижний уровень параллелизма обозначен в горизонтальных плоскостях. Множество всех плоскостей представляет собой верхний уровень параллелизма (операции в каждой плоскости могут выполняться параллельно).&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма расположен на уровнях [2] и [3], соответствующим операциям инициализации массива дистанций [2] и обновления массива c использованием данных массива ребер [3]. Операция [4] - проверка того, были ли изменения на последней итерации и выход из цикла, если таковых не было.&lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма, как уже говорилось, заключается в параллельном подсчете дистанций для различных вершин-источников, и на рисунке отмечен разными плоскостями.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм обладает значительным ресурсом параллелизма. Во-первых, поиск кратчайших путей от различных вершин может производиться независимо для каждой из вершин (параллельные вертикальные плоскости на рисунке 1). Во-вторых, поиск кратчайших путей от фиксированной вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; также может выполняться параллельно: инициализация начальных путей [2] требует &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; параллельных операции, релаксация каждого ребра требует &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt; параллельных операции. &lt;br /&gt;
&lt;br /&gt;
Таким образом, при наличии &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt; процессоров алгоритм завершит работу максимум за &amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; шагов. В реальности, шагов обычно требуется меньше, а именно &amp;lt;math&amp;gt;O(r)&amp;lt;/math&amp;gt; -(максимальная длина среди всех кратчайших путей от выбранной вершины-источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Таким образом, ширина ярусно-параллельной формы алгоритма равна &amp;lt;math&amp;gt;O(|E|)&amp;lt;/math&amp;gt;, высота ЯПФ - &amp;lt;math&amp;gt;O(r) | r &amp;lt; |V|&amp;lt;/math&amp;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;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;|V|&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V| + |E|)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(|V|)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Алгоритм может распознавать наличие отрицательных циклов в графе. Ребро &amp;lt;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(|E|)&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;
[[file:bellman_ford_1.png|thumb|center|700px|Рисунок 2. Алгоритм Беллмана-Форда. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации алгоритма Беллмана-Форда. Первое, что сразу стоит отметить – число обращений в память гораздо больше числа задействованных данных. Это говорит о частом повторном использовании одних и тех же данных, что обычно приводит к высокой временной локальности. Далее, видна явная регулярная структура производимых обращений в память, видны повторяющиеся итерации работы алгоритма. Практически все обращения образуют фрагменты, похожие на последовательный перебор, кроме самой верхней части, где наблюдается более сложная структура.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим более детально отдельные фрагменты общего профиля, чтобы лучше разобраться в его структуре. На рис. 3 представлен фрагмент 1 (выделен на рис. 2), на котором показаны первые 500 обращений в память (отметим, что другой наклон двух последовательных переборов связан с измененным отношением сторон в рассматриваемой области). Данный рисунок показывает, что выделенные желтым части 1 и 2 являются практически идентичными последовательными переборами; отличие между ними только в том, что в части 1 обращения выполняются в два раза чаще, поэтому на рис. 3 эта часть представлена большим числом точек. Как мы знаем, подобные профили характеризуются высокой пространственной и низкой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим более интересный фрагмент 2, отмеченный на рис. 2 (см. рис. 4). Здесь можно снова увидеть подтверждение регулярности обращений в нижней области профиля, однако верхняя область явно устроена гораздо сложнее; хотя и здесь просматривается регулярность. В частности, также видны те же самые итерации, в которых здесь можно выделить большие последовательности обращений к одним и тем же данным. Пример такого поведения, оптимального с точки зрения локальности, выделен на рисунке желтым. &lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_3.png|thumb|center|700px|Рисунок 4. Профиль обращений, фрагмент 2]]&lt;br /&gt;
&lt;br /&gt;
Чтобы понять структуру обращений в память в верхней части, можно рассмотреть ее еще подробнее. Приведем визуализацию небольшой области фрагмента 2, выделенной на рис. 4 зеленым. Однако в данном случае дальнейшее приближение (рис. 5) не привносит большей ясности: видна нерегулярная структура внутри итерации, характер которой достаточно сложно описать. Но в данном случае этого и не требуется – можно заметить, что по вертикали отложено всего 15 элементов, при этом обращений к ним выполняется гораздо больше. Независимо от структуры обращений, такой профиль обладает очень высокой как пространственной, так и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_4.png|thumb|center|700px|Рисунок 5. Небольшая часть фрагмента 2]]&lt;br /&gt;
&lt;br /&gt;
А так как основная масса обращений приходится именно на фрагмент 2, можно утверждать, что и весь общий профиль обладает высокой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что по производительности работы с памятью данная реализация алгоритма показывает очень хорошие результаты. В частности, значение daps сравнимо с оценкой для теста Linpack, который известен высокой эффективностью взаимодействия с подсистемой памяти. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:bellman_ford_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая алгоритм поиска кратчайших путей, состоит из двух частей: части, отвечающей за общую координацию вычислений, а также параллельные вычисления на многоядерных CPU, и GPU части, отвечающей только за вычисления на графическом ускорителе.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Алгоритм обладает значительным потенциалом масштабируемости, так как каждое ребро обрабатывается независимо и можно поручить каждому вычислительному процессу свою часть рёбер графа. Узким местом является доступ к разделяемому всеми процессами массиву расстояний. Алгоритм позволяет ослабить требования к синхронизации данных этого массива между процессами (когда один процесс может не сразу увидеть новое значение расстояния, записанное другим процессом), за счёт, может быть, большего количества глобальных итераций.&lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Беллмана-Форда согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости вширь реализации алгоритма Беллмана-Форда.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера.&lt;br /&gt;
&lt;br /&gt;
[[file:Сильная_масштабируемость_вширь_алгоритма_Беллмана-Форда.png|thumb|center|700px|Рисунок 7. Параллельная реализация алгоритма Беллмана-Форда масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация алгоритма Беллмана-Форда, реализованная для CPU.  Все результаты получены на суперкомпьютере «Ломоносов-2». Использовались процессоры Intel Xeon E5-2697v3, задача решалась для графа большого размера на одном узле. &lt;br /&gt;
На рисунках показана эффективность реализации алгоритма Беллмана-Форда, запуск проводился на 1 узле для графа 2^27, выполнялась 1 итерация.&lt;br /&gt;
[[file:Bellman-Ford cpu.png|thumb|center|700px|Рисунок 8. График загрузки CPU при выполнении алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы не загружены и средний уровень загрузки составляет около 5%. Это достаточно неэффективная картина даже для программ, запущенных c использованием одного ядра в реализации. &lt;br /&gt;
&lt;br /&gt;
[[file:Bellman-Ford LoadAvg.png|thumb|center|700px|Рисунок 9. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно на уровне 1. Это указывает на постоянную загрузку аппаратных ресурсов не более чем 1 процессом, однако это число для узла слишком мало, что может указывать на не очень рациональное использование ресурсов.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford L1.png|thumb|center|700px|Рисунок 10. График кэш-промахов L1 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов очень высокое и находится на уровне 140 млн/сек. Это очень высокий показатель, показывающий потенциальную причину неэффективности.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford L2.png|thumb|center|700px|Рисунок 11. График кэш-промахов L2 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов второго уровня видно, что число таких промахов тоже крайне высокое и находится на уровне 140 млн/сек, что указывает на крайне неэффективную работу с памятью.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford-L3.png|thumb|center|700px|Рисунок 12. График кэш-промахов L3 в секунду при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов последнего уровня видно, что число промахов тоже достаточно большое и составляет около 30 млн/сек по всем узлам. Это указывает на то, что задача очень плохо укладывается в кэш-память, и программа постоянно работает с оперативной памятью, что объясняется очень большим размером использованного графа.&lt;br /&gt;
&lt;br /&gt;
[[file:Bellman-Ford Inf Data.png|thumb|center|700px|Рисунок 13. График скорости передачи по сети Infiniband в байт/сек при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно высокая интенсивность использования сети на первом этапе. Это объясняется программной логикой, которая предполагает чтение графа из файла с диска, коммуникации с которым происходят на Ломоносов-2 через выделенную для этих задач сеть Infiniband.&lt;br /&gt;
&lt;br /&gt;
 [[file:Bellman-Ford Inf Pckts.png|thumb|center|700px|Рисунок 14. График скорости передачи по сети Infiniband в пакетах/сек при работе алгоритма Беллмана-Форда]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается аналогичная картина очень высокой интенсивности на первом этапе выполнения задачи. Далее сеть почти не используется.&lt;br /&gt;
 &lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала стабильно интенсивно,  однако очень неэффективно использовала память из-за очень большого размера графа.&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En:Bellman-Ford algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%BE%D1%82_%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B_(SSSP)&amp;diff=25614</id>
		<title>Поиск кратчайшего пути от одной вершины (SSSP)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%BE%D1%82_%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B_(SSSP)&amp;diff=25614"/>
		<updated>2018-06-14T13:12:06Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-p}}&lt;br /&gt;
&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/5}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
== Постановка задачи ==&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Последовательность &amp;lt;math&amp;gt;P(u, v)&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_1 = (u, w_1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e_2 = (w_1, w_2)&amp;lt;/math&amp;gt;, …, &amp;lt;math&amp;gt;e_k = (w_{k-1}, v)&amp;lt;/math&amp;gt; называется &#039;&#039;путём&#039;&#039;, идущим от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к вершине &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Суммарный вес этого пути равен&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f(P(u, v)) = \sum_{i = 1}^k f(e_i).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Требуется для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, достижимой из вершины-источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, указать путь &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt;, имеющий наименьший возможный суммарный вес:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f^*(v) = f(P^*(u, v)) = \min f(P(u, v)).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Название задачи на английском языке – «Single Source Shortest Path» (SSSP).&lt;br /&gt;
&lt;br /&gt;
== Варианты задачи ==&lt;br /&gt;
&lt;br /&gt;
Если требуется найти кратчайшие пути не от одной, а от всех вершин - см. [[Поиск кратчайшего пути для всех пар вершин (APSP)|поиск кратчайшего пути для всех пар вершин]].&lt;br /&gt;
&lt;br /&gt;
Задача может быть поставлена как для ориентированного, так и для неориентированного графа. Приведённая постановка задачи применима в обоих случаях.&lt;br /&gt;
&lt;br /&gt;
В зависимости от ограничений на возможные значения весов различают следующие случаи.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Единичные веса&#039;&#039;&#039;. В этом случае задача существенно упрощается и может быть решена за линейное время [[Поиск в ширину (BFS)|алгоритмом поиска вширь]].&lt;br /&gt;
* &#039;&#039;&#039;Положительные целые веса&#039;&#039;&#039;. Задача также может быть решена за линейное время&amp;lt;ref name=thorup&amp;gt;Thorup, Mikkel. “Undirected Single-Source Shortest Paths with Positive Integer Weights in Linear Time.” Journal of the ACM 46, no. 3 (May 1, 1999): 362–94. doi:10.1145/316542.316548.&amp;lt;/ref&amp;gt; (хотя и с большей константой).&lt;br /&gt;
* &#039;&#039;&#039;Неотрицательные веса&#039;&#039;&#039;. Часто встречающееся на практике ограничение, когда вес ребра соответствует времени или стоимости прохождения участка маршрута и не может быть отрицательным.&lt;br /&gt;
* &#039;&#039;&#039;Произвольные веса&#039;&#039;&#039;. Наиболее общая постановка. Граф не должен содержать циклов с отрицательным суммарным весом, иначе кратчайшего пути не существует.&lt;br /&gt;
&lt;br /&gt;
== Свойства задачи ==&lt;br /&gt;
&lt;br /&gt;
Решение задачи удовлетворяет принципу оптимальности: если путь &amp;lt;math&amp;gt;P^*(u, w)&amp;lt;/math&amp;gt; является частью кратчайшего пути &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;P^*(u, w)&amp;lt;/math&amp;gt; является кратчайшим путём от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Принцип оптимальности означает, что для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; вместо всего кратчайшего пути &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt; достаточно хранить его последнее ребро &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt;. Отсюда следует оценка объёма памяти &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Описание входных и выходных данных ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Преобразование выходных данных и поиск кратчайшего пути ===&lt;br /&gt;
&lt;br /&gt;
Кратчайший путь от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; может быть восстановлен за время &amp;lt;math&amp;gt;O(\left |P^*(u, v) \right |)&amp;lt;/math&amp;gt;, если, начиная с вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, проходить рёбра &amp;lt;math&amp;gt;e^*_w&amp;lt;/math&amp;gt; в обратном направлении до тех пор, пока не будет посещена вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рёбра &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt; могут быть восстановлены за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; перебором рёбер для каждой вершины:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
    e^*_v \in \{ (w, v) \in E \mid f^*(v) = f^*(w) + f((v, w)) \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Перебор может осуществляться параллельно.&lt;br /&gt;
&lt;br /&gt;
Для поиска кратчайших расстояний &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; по набору рёбер &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt;, необходимо построить из них дерево, на котором выполнить [[Поиск в ширину (BFS)|поиск в ширину]] за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (с возможностью параллелизации).&lt;br /&gt;
&lt;br /&gt;
== Алгоритмы решения задачи ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[[Поиск в ширину (BFS)]]&#039;&#039;&#039; для ориентированных невзвешенных графов, сложность &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Дейкстры]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1, 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Беллмана-Форда]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; для ориентированных графов с произвольными весами, сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Δ-шагания]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Meyer, U, and P Sanders. “Δ-Stepping: a Parallelizable Shortest Path Algorithm.” Journal of Algorithms 49, no. 1 (October 2003): 114–52. doi:10.1016/S0196-6774(03)00076-2.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, средняя сложность на графах со случайными весами &amp;lt;math&amp;gt;O(n + m + dL)&amp;lt;/math&amp;gt;, параллельная сложность &amp;lt;math&amp;gt;O((dL + \ln n) \ln n&amp;lt;/math&amp;gt; со средней работой &amp;lt;math&amp;gt;O(n + m + dL \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Алгоритм Торупа&#039;&#039;&#039;&amp;lt;ref name=thorup /&amp;gt; представляет собой теоретическое доказательство возможности решения задачи для неориентированных графов с положительными целыми весами за линейное время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В ориентированном ациклическом графе с произвольными весами время поиска кратчайших путей линейное – &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;, алгоритм основан на топологической сортировке графа.&lt;br /&gt;
&lt;br /&gt;
Обозначения: &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; – число рёбер, &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; – число вершин, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; – максимальная степень вершины, &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; – максимальный суммарный вес кратчайшего пути.&lt;br /&gt;
&lt;br /&gt;
Перечисленные алгоритмы могут использоваться и для решения [[Поиск кратчайшего пути для всех пар вершин (APSP)|задачи поиска кратчайшего пути для всех пар вершин графа]] (для этого необходимо запустить соответствующий алгоритм для каждой вершины графа, что при необходимости можно сделать параллельно), при этом оценка сложности умножается на &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En: Single Source Shortest Path (SSSP)]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=25612</id>
		<title>Алгоритм Дейкстры</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=25612"/>
		<updated>2018-06-14T12:42:16Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-a}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Daryin|А.Н.Дарьин]], [[Участник:VadimVV|Вад.В.Воеводин]] ([[#Описание локальности данных и вычислений|раздел 2.2]])&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Дейкстры&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа.&lt;br /&gt;
Алгоритм Дейкстры (с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt;) выполняется за время &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt; и является асимптотически быстрейшим из известных последовательных алгоритмов для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть уже вычислены все расстояния, не превосходящие некоторого числа &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;, то есть расстояния до вершин из множества &amp;lt;math&amp;gt;V_r = \{ v \in V \mid d(v) \le r \}&amp;lt;/math&amp;gt;. Пусть&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        (v, w) \in \arg\min \{ d(v) + f(e) \mid  v \in V, e = (v, w) \in E \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Тогда &amp;lt;math&amp;gt;d(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; лежит на кратчайшем пути от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Величины &amp;lt;math&amp;gt;d^+(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v \in V_r&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e = (v, w) \in E&amp;lt;/math&amp;gt;, называются &#039;&#039;предполагаемыми расстояниями&#039;&#039; и являются оценкой сверху для настоящих расстояний: &amp;lt;math&amp;gt;d(w) \le d^+(w)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры на каждом шаге находит вершину с наименьшим предполагаемым расстоянием, помечает её как посещённую и обновляет предполагаемые расстояния для всех концов рёбер, исходящих из неё.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основные вычисления связаны с операциями над очередью с приоритетом:&lt;br /&gt;
* извлечение минимального элемента (&amp;lt;code&amp;gt;delete_min&amp;lt;/code&amp;gt;);&lt;br /&gt;
* уменьшение приоритета элемента (&amp;lt;code&amp;gt;decrease_key&amp;lt;/code&amp;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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;Q&#039;&#039; := &#039;&#039;&#039;new&#039;&#039;&#039; priority queue&lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;:&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;v&#039;&#039; = &#039;&#039;u&#039;&#039; &#039;&#039;&#039;then&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := 0 &#039;&#039;&#039;else&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞ &lt;br /&gt;
     &#039;&#039;Q&#039;&#039;.insert(&#039;&#039;v&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;))&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;while&#039;&#039;&#039; &#039;&#039;Q&#039;&#039; ≠ ∅:&lt;br /&gt;
     &#039;&#039;v&#039;&#039; := &#039;&#039;Q&#039;&#039;.delete_min()&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
             &#039;&#039;Q&#039;&#039;.decrease_key(&#039;&#039;w&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;))&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Конкретная реализация алгоритма Дейкстры определяется выбором используемого алгоритма очереди с приоритетом. В простейшем случае это может быть массив или список, поиск минимума в котором требует просмотра всех вершин. Более эффективным является использование кучи; наилучшую известную оценку сложности имеет вариант с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Возможен вариант реализации, когда вершины добавляются в очередь не на этапе инициализации, а в момент первого посещения.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательная сложность алгоритма равна &amp;lt;math&amp;gt;O(C_1 m + C_2n)&amp;lt;/math&amp;gt;, где&lt;br /&gt;
* &amp;lt;math&amp;gt;C_1&amp;lt;/math&amp;gt; – количество операций уменьшения расстояния до вершины;&lt;br /&gt;
* &amp;lt;math&amp;gt;C_2&amp;lt;/math&amp;gt; – количество операций вычисления минимума.&lt;br /&gt;
&lt;br /&gt;
Оригинальный алгоритм Дейкстры использовал в качестве внутренней структуры данных списки, для которых &amp;lt;math&amp;gt;C_1 = O(1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;C_2 = O(n)&amp;lt;/math&amp;gt;, так что общая сложность составляла &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При использовании фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt; время вычисления минимума сокращается до &amp;lt;math&amp;gt;C_2 = O(\ln n)&amp;lt;/math&amp;gt;, так что общая сложность равна &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;, что является асимптотически наилучшим известным результатом для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
Приводится граф алгоритма для базовой реализации алгоритма Дейкстры на списках или массивах.&lt;br /&gt;
[[file:Deikstra.png|thumb|center|600px|Рисунок 1. Граф алгоритма без отображения входных и выходных данных. n=3. Желтым цветом обозначены операции сравнения, зеленым - операции изменения меток вершин, синим - пометка вершины.]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры допускает эффективную параллелизацию&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;, среднее время работы &amp;lt;math&amp;gt;O(n^{1/3}\ln n)&amp;lt;/math&amp;gt; с объёмом вычислений &amp;lt;math&amp;gt;O(n \ln n + m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Дейкстры.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(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;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_1.png|thumb|center|700px|Рисунок 1. Реализация алгоритма Дейкстры. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 1 представлен профиль обращений в память для реализации алгоритма Дейкстры. Первое, что бросается в глаза, – большая разрозненность обращений. В частности, значительные области выше и ниже фрагмента 2 остаются пустыми, при этом сами обращения объединены лишь в небольшие группы. Это говорит о низкой эффективности, поскольку: а) повторные обращения практически отсутствуют либо происходят через значительный промежуток времени; б) расстояния между идущими подряд обращениями может быть очень большим.&lt;br /&gt;
&lt;br /&gt;
Однако при ближайшем рассмотрении может оказаться, что такие участки обладают высокой локальностью и состоят из значительного числа обращений. Более того, на общем профиле есть несколько областей (фрагменты 1 и 2), в которых обращения хорошо локализованы. Необходимо исследовать отдельные участки более подробно.&lt;br /&gt;
&lt;br /&gt;
Перейдем к изучению фрагмента 1 (рис. 2), в рамках которого выполняются обращения к двум небольшим массивам. Можно увидеть, что здесь задействованы только примерно 500 элементов, при этом к ним выполняется около 100 тысяч обращений. Весь профиль составляет около 120 тысяч обращений, поэтому получается, что подавляющая их часть выполняется именно к этим элементам. &lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_2.png|thumb|center|700px|Рисунок 2. Профиль обращений, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Поскольку число элементов невелико, локальность в данном случае заведомо будет достаточно высокой, независимо от структуры самого фрагмента.  Однако на рис. 3, где представлены два участка из фрагмента 1, можно увидеть, что фрагмент в основном состоит из последовательных переборов, при этом данные зачастую задействованы повторно через не очень большие промежутки. Все это позволяет говорить, что данный фрагмент обладает высокой как пространственной, так и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_3.png|thumb|center|700px|Рисунок 3. Профили двух участков фрагмента 1 (выделены зеленым на рис. 2)]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим теперь более подробно фрагмент 2 (рис. 4), в рамках которого выполняются обращения к еще служебному массиву. Здесь профиль состоит из двух этапов. На первом заметен достаточно хаотичный разброс обращений, напоминающий случайный доступ. На втором обращения образуют подобие последовательного перебора.  В целом такой профиль характеризуется очень низкой временной локальностью (повторные обращения практически или полностью отсутствуют) и достаточно низкой пространственной локальностью (из-за случайного доступа на первом этапе).&lt;br /&gt;
&lt;br /&gt;
Заметим, что при этом число задействованных элементов здесь больше, чем во фрагменте 1, однако число обращений гораздо меньше.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_4.png|thumb|center|500px|Рисунок 4. Профиль обращений, фрагмент 2]]&lt;br /&gt;
&lt;br /&gt;
Далее остаются для рассмотрения два массива (область между фрагментами 1 и 2 и область ниже фрагмента 2). Характер обращений к этим массивам во многом похож, поэтому достаточно изучить более подробно только один из них. &lt;br /&gt;
&lt;br /&gt;
Фрагмент 3 рассмотрен на рис. 5. Этот участок отражаeт достаточно большую область, что не позволяет проанализировать профиль вплоть до отдельных обращений, однако здесь этого и не требуется. Из данного фрагмента видно, что основу профиля составляют участки с последовательным (или с небольшим шагом) перебором, состоящие из небольшого числа элементов – выделенный желтым самый большой участок фрагмента состоит всего из пары сотен обращений. При этом между разными участками расстояние может быть существенным. Все это говорит об очень низкой локальности (как пространственной, так и временной) в случае двух рассматриваемых массивов.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_5.png|thumb|center|500px|Рисунок 5. Профиль обращений, фрагмент 3]]&lt;br /&gt;
&lt;br /&gt;
В целом, несмотря на положительный вклад массивов из фрагмента 1, локальность общего профиля должна быть достаточно низкой, вследствие неэффективного использования данных в остальной части профиля.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Основной фрагмент реализации, на основе которого были получены количественные оценки, приведен [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/benchmarks/dijkstra/dijkstra.h здесь] (функция Kernel). Условия запуска описаны [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/README.md здесь].&lt;br /&gt;
&lt;br /&gt;
Первая оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью достаточно низка. Это неудивительно, поскольку реализации алгоритмов над графами почти всегда обладают низкой эффективностью вследствие нерегулярности доступа к данным, что мы и увидели при анализе профиля обращений.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
Вторая характеристика – cvg – предназначена для получения более машинно-независимой оценки локальности. Она определяет, насколько часто в программе необходимо подтягивать данные в кэш-память. Соответственно, чем меньше значение cvg, тем реже это нужно делать, тем лучше локальность. &lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения cvg для того же набора реализаций, отсортированные по убыванию (чем меньше cvg, тем в общем случае выше локальность). Можно увидеть, что в данном случае значение cvg хорошо коррелирует с оценкой производительности и отражает низкую локальность, что соответствует выводам, сделанным при качественной оценке локальности.&lt;br /&gt;
&lt;br /&gt;
[[file:dijkstra_cvg.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки cvg]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot;&amp;lt;ref name=&amp;quot;Lom&amp;quot;&amp;gt;Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&amp;lt;/ref&amp;gt; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [4, 8 : 128] со значениями квадрата целого числа;&lt;br /&gt;
*	размер графа [16000:64000] с шагом 16000.&lt;br /&gt;
&lt;br /&gt;
На следующем рисунке приведен график [[Глоссарий#Производительность|производительности]] выбранной реализации алгоритма в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra perf.png|thumb|center|700px|Рисунок 8. Параллельная реализация алгоритма. Изменение производительности в зависимости от числа процессоров и размера области.]]&lt;br /&gt;
&lt;br /&gt;
В силу особенностей параллельной реализации алгоритма производительность в целом достаточно низкая и с ростом числа процессов увеличивается медленно, а при приближении к числу процессов 128 начинает уменьшаться.&lt;br /&gt;
Это объясняется использованием коллективных операций на каждой итерации алгоритма и тем, что затраты на коммуникационные обмены существенно возрастают с ростом числа использованных процессов. Вычисления на каждом процессе проходят достаточно быстро и потому декомпозиция графа слабо компенсирует эффект от затрат на коммуникационные обмены. &lt;br /&gt;
&lt;br /&gt;
[https://code.google.com/p/prir-dijkstra-mpi/source/browse/trunk/project/dijkstra_mpi.c Исследованная параллельная реализация на языке C]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация алгоритма Дейкстры.  Все результаты получены на суперкомпьютере &amp;quot;Ломоносов&amp;quot;. Использовались процессоры Intel Xeon X5570 с пиковой производительностью в 94 Гфлопс, а также компилятор intel 13.1.0. &lt;br /&gt;
На рисунках показана эффективность реализации алгоритма Дейкстры  на 32 процессах.&lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra CPU User.png|thumb|center|700px|Рисунок 9. График загрузки CPU при выполнении алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы уровень загрузки составляет около 50%. Это указывает на равномерную загруженность вычислениями процессоров, при использовании 8 процессов на вычислительный узел и без использования Hyper Threading. &lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra Flops.png|thumb|center|700px|Рисунок 10. График операций с плавающей точкой в секунду при выполнении алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На Рисунке 10 показан график количества операций с плавающей точкой в секунду. На графике видна общая очень низкая производительность вычислений около 250 Kфлопс в пике и около 150 Кфлопс в среднем по всем узлам. Это указывает то, что в программе почти все вычисления производятся с целыми числами. &lt;br /&gt;
 [[file:Dijkstra L1.png|thumb|center|700px|Рисунок 11. График кэш-промахов L1 в секунду при работе алгоритма Дейкстры]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов очень большое для нескольких ядер и находится на уровне 15 млн/сек (в пике до 60 млн/сек), что указывает на интенсивные вычисления в части процессов. В среднем по всем узлам значения значительно ниже (около 9 млн/сек). Это указывает на неравномерное распределение вычислений.&lt;br /&gt;
 [[file:Dijkstra L3.png|thumb|center|700px|Рисунок 12. График кэш-промахов L3 в секунду при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов третьего уровня видно, что число промахов очень невелико и находится на уровне 1,2 млн/сек, однако в среднем по всем узлам значения около 0,5 млн/сек. Соотношение кэш промахов L1|L3 для процессов с высокой производительностью доходит до 60, однако в среднем около 30. Это указывает на очень хорошую локальность вычислений как у части процессов, так и для всех в среднем, и это является признаком высокой производительности. &lt;br /&gt;
&lt;br /&gt;
[[file:Dijkstra Mem Load.png|thumb|center|700px|Рисунок 13. График количества чтений из оперативной памяти при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике обращений в память видна достаточно типичная картина для таких приложений. Активность чтения достаточно низкая, что в совокупности с низкими значениями кэш-промахов L3 указывает на хорошую локальность. Хорошая локальность приложения также указывает на то, что значение около 1 млн/сек для задачи является результатом высокой производительности вычислений, хотя и присутствует неравномерность между процессами.&lt;br /&gt;
 [[file:Dijkstra Mem Store.png|thumb|center|700px|Рисунок 14. График количества записей в оперативную память при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике записей в память видна похожая картина неравномерности вычислений, при которой одновременно активно выполняют запись только несколько процессов. Это коррелирует с другими графиками выполнения. Стоит отметить достаточно низкое число обращений на запись в память. Это указывает на хорошую организацию вычислений и достаточно эффективную работу с памятью.&lt;br /&gt;
 [[file:Dijkstra Inf DATA.png|thumb|center|700px|Рисунок 15. График скорости передачи по сети Infiniband в байт/сек при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно высокая скорость передачи данных в байтах в секунду. Это говорит о том, что процессы между собой обмениваются интенсивно и вероятно достаточно малыми порциями данных, потому как производительность вычислений высока. Стоит отметить, что скорость передачи отличается между процессами, что указывает на дисбаланс вычислений.&lt;br /&gt;
 [[file:Dijkstra Inf PCKTS.png|thumb|center|700px|Рисунок 16. График скорости передачи по сети Infiniband в пакетах/сек при работе алгоритма Дейкстры]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается крайне высокая интенсивность передачи данных. Это говорит о том, что, вероятно, процессы обмениваются не очень существенными объемами данных, но очень интенсивно. Используются коллективные операции на каждом шаге с небольшими порциями данных, что объясняет такую картину.  Также наблюдается меньший дизбаланс между процессами, чем наблюдаемый в графиках использования памяти, вычислений и передачи данных в байтах/сек. Это указывает на то, что процессы обмениваются по алгоритму одинаковым числом пакетов, однако получают разные объемы данных и ведут неравномерные вычисления. &lt;br /&gt;
 [[file:Dijkstra Load ABG.png|thumb|center|700px|Рисунок 17. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе алгоритма Дейкстры]]&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно и приблизительно равняется 8. Это свидетельствует о стабильной работе программы с загруженными вычислениями всеми узлами. Это указывает на очень рациональную и статичную загрузку аппаратных ресурсов процессами. И показывает достаточно хорошую эффективность выполняемой реализации.&lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала достаточно эффективно, и стабильно. Использование памяти  очень интенсивное, а использование коммуникационной среды крайне интенсивное, при этом объемы передаваемых данных не являются высокими. Это указывает на требовательность к латентности коммуникационной среды алгоритмической части программы. &lt;br /&gt;
Низкая эффективность связана, судя по всему, с достаточно высоким объемом пересылок на каждом процессе и с интенсивными обменами сообщениями.&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths.html dijkstra_shortest_paths]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths_no_color_map.html dijkstra_shortest_paths_no_color_map]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. &lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]:&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#eager-dijkstra-s-algorithm eager_dijkstra_shortest_paths]&amp;lt;/code&amp;gt; – непосредственная реализация алгоритма Дейкстры;&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#crauser-et-al-s-algorithm crauser_et_al_shortest_paths]&amp;lt;/code&amp;gt; – реализация алгоритма Дейкстры в виде алгоритма из статьи Краузера и др.&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/implementation/14}}&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.single_source_dijkstra.html single_source_dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python/C++: [https://networkit.iti.kit.edu NetworKit] (класс &amp;lt;code&amp;gt;[https://networkit.iti.kit.edu/data/uploads/docs/NetworKit-Doc/python/html/graph.html#networkit.graph.Dijkstra networkit.graph.Dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En:Dijkstra&#039;s algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%CE%94-%D1%88%D0%B0%D0%B3%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=25611</id>
		<title>Алгоритм Δ-шагания</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%CE%94-%D1%88%D0%B0%D0%B3%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=25611"/>
		<updated>2018-06-14T12:25:57Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-a}}&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм дельта-шагания&#039;&#039;&#039;&amp;lt;ref&amp;gt;Meyer, U, and P Sanders. “Δ-Stepping: a Parallelizable Shortest Path Algorithm.” Journal of Algorithms 49, no. 1 (October 2003): 114–52. doi:10.1016/S0196-6774(03)00076-2.&amp;lt;/ref&amp;gt; (англ. Δ-Stepping) предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм изначально проектировался с целью эффективной параллелизации.&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;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;W&#039;&#039;;&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;;&lt;br /&gt;
   параметр &#039;&#039;Δ&#039;&#039; &amp;gt; 0.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; DeltaStepping(&#039;&#039;V&#039;&#039;, &#039;&#039;E&#039;&#039;, &#039;&#039;W&#039;&#039;, &#039;&#039;u&#039;&#039;, &#039;&#039;Δ&#039;&#039;):&lt;br /&gt;
      &#039;&#039;LightEdges&#039;&#039; := { e ∈ E | W(e) ≤ Δ }&lt;br /&gt;
      &#039;&#039;HeavyEdges&#039;&#039; := { e ∈ E | W(e) &amp;gt; Δ }&lt;br /&gt;
      &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
      Relax(&#039;&#039;u&#039;&#039;, 0)&lt;br /&gt;
      &#039;&#039;&#039;while&#039;&#039;&#039; any({ &#039;&#039;Buckets&#039;&#039;(i) ≠ ∅ }):&lt;br /&gt;
          &#039;&#039;Bucket&#039;&#039; := first({ &#039;&#039;Buckets&#039;&#039;(i) ≠ ∅ })&lt;br /&gt;
          &#039;&#039;Deleted&#039;&#039; := ∅&lt;br /&gt;
          &#039;&#039;&#039;while&#039;&#039;&#039; &#039;&#039;Bucket&#039;&#039; ≠ ∅:&lt;br /&gt;
              &#039;&#039;Requests&#039;&#039; := FindRequests(&#039;&#039;Bucket&#039;&#039;, &#039;&#039;LightEdges&#039;&#039;)&lt;br /&gt;
              &#039;&#039;Deleted&#039;&#039; := &#039;&#039;Deleted&#039;&#039; ∪ &#039;&#039;Bucket&#039;&#039;&lt;br /&gt;
              &#039;&#039;Bucket&#039;&#039; := ∅&lt;br /&gt;
              RelaxAll(&#039;&#039;Requests&#039;&#039;)&lt;br /&gt;
          RelaxAll(FindRequests(&#039;&#039;Deleted&#039;&#039;, &#039;&#039;HeavyEdges&#039;&#039;))&lt;br /&gt;
          &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; Relax(&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;x&#039;&#039; &amp;lt; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;):&lt;br /&gt;
         &#039;&#039;OldBucket&#039;&#039; := B(⌊&#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) / &#039;&#039;Δ&#039;&#039;⌋)&lt;br /&gt;
         &#039;&#039;NewBucket&#039;&#039; := B(⌊&#039;&#039;x&#039;&#039; / &#039;&#039;Δ&#039;&#039;⌋)&lt;br /&gt;
         &#039;&#039;OldBucket&#039;&#039; := &#039;&#039;OldBucket&#039;&#039; \ {&#039;&#039;v&#039;&#039;}&lt;br /&gt;
         &#039;&#039;NewBucket&#039;&#039; := &#039;&#039;NewBucket&#039;&#039; ∪ {&#039;&#039;v&#039;&#039;}&lt;br /&gt;
         &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;x&#039;&#039;&lt;br /&gt;
         &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; RelaxAll(&#039;&#039;R&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; (&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;) ∈ &#039;&#039;R&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; Relax(&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;)&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; FindRequests(&#039;&#039;V&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;, &#039;&#039;E&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;return&#039;&#039;&#039; { (&#039;&#039;w&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;W&#039;&#039;(&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;)) | &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039; and (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;}&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Средняя последовательная сложность алгоритма на графах со случайными весами &amp;lt;math&amp;gt;O(n + m + dL)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; – максимальный суммарный вес кратчайшего пути, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; – максимальная длина кратчайшего пути.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Средняя время работы параллельного алгоритма на графах со случайными весами на &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; процессорах составляет &amp;lt;math&amp;gt;O((dL + \ln n) \ln n)&amp;lt;/math&amp;gt; со средней работой &amp;lt;math&amp;gt;O(n + m + dL \ln n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Предельные случаи алгоритма Δ-шагания:&lt;br /&gt;
* при &amp;lt;math&amp;gt;\Delta \to 0&amp;lt;/math&amp;gt;: [[алгоритм Дейкстры]];&lt;br /&gt;
* при &amp;lt;math&amp;gt;\Delta \to \infty&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;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#delta-stepping-algorithm delta_stepping_shortest_paths]&amp;lt;/code&amp;gt;).&lt;br /&gt;
{{Buttonlink|http://top53.parallel.ru/algo_results/implementation/25}}&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;br /&gt;
&lt;br /&gt;
[[en:Δ-stepping algorithm]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%BE%D1%82_%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B_(SSSP)&amp;diff=25610</id>
		<title>Поиск кратчайшего пути от одной вершины (SSSP)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%BE%D1%82_%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D1%8B_(SSSP)&amp;diff=25610"/>
		<updated>2018-06-14T12:22:09Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{level-p}}&lt;br /&gt;
&amp;lt;indicator name=&#039;toplink&#039;&amp;gt; {{Buttonlink|http://top53.parallel.ru/algo_results/task/29}} &amp;lt;/indicator&amp;gt;&lt;br /&gt;
== Постановка задачи ==&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Последовательность &amp;lt;math&amp;gt;P(u, v)&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_1 = (u, w_1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e_2 = (w_1, w_2)&amp;lt;/math&amp;gt;, …, &amp;lt;math&amp;gt;e_k = (w_{k-1}, v)&amp;lt;/math&amp;gt; называется &#039;&#039;путём&#039;&#039;, идущим от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к вершине &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Суммарный вес этого пути равен&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f(P(u, v)) = \sum_{i = 1}^k f(e_i).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Требуется для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, достижимой из вершины-источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, указать путь &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt;, имеющий наименьший возможный суммарный вес:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f^*(v) = f(P^*(u, v)) = \min f(P(u, v)).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Название задачи на английском языке – «Single Source Shortest Path» (SSSP).&lt;br /&gt;
&lt;br /&gt;
== Варианты задачи ==&lt;br /&gt;
&lt;br /&gt;
Если требуется найти кратчайшие пути не от одной, а от всех вершин - см. [[Поиск кратчайшего пути для всех пар вершин (APSP)|поиск кратчайшего пути для всех пар вершин]].&lt;br /&gt;
&lt;br /&gt;
Задача может быть поставлена как для ориентированного, так и для неориентированного графа. Приведённая постановка задачи применима в обоих случаях.&lt;br /&gt;
&lt;br /&gt;
В зависимости от ограничений на возможные значения весов различают следующие случаи.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Единичные веса&#039;&#039;&#039;. В этом случае задача существенно упрощается и может быть решена за линейное время [[Поиск в ширину (BFS)|алгоритмом поиска вширь]].&lt;br /&gt;
* &#039;&#039;&#039;Положительные целые веса&#039;&#039;&#039;. Задача также может быть решена за линейное время&amp;lt;ref name=thorup&amp;gt;Thorup, Mikkel. “Undirected Single-Source Shortest Paths with Positive Integer Weights in Linear Time.” Journal of the ACM 46, no. 3 (May 1, 1999): 362–94. doi:10.1145/316542.316548.&amp;lt;/ref&amp;gt; (хотя и с большей константой).&lt;br /&gt;
* &#039;&#039;&#039;Неотрицательные веса&#039;&#039;&#039;. Часто встречающееся на практике ограничение, когда вес ребра соответствует времени или стоимости прохождения участка маршрута и не может быть отрицательным.&lt;br /&gt;
* &#039;&#039;&#039;Произвольные веса&#039;&#039;&#039;. Наиболее общая постановка. Граф не должен содержать циклов с отрицательным суммарным весом, иначе кратчайшего пути не существует.&lt;br /&gt;
&lt;br /&gt;
== Свойства задачи ==&lt;br /&gt;
&lt;br /&gt;
Решение задачи удовлетворяет принципу оптимальности: если путь &amp;lt;math&amp;gt;P^*(u, w)&amp;lt;/math&amp;gt; является частью кратчайшего пути &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;P^*(u, w)&amp;lt;/math&amp;gt; является кратчайшим путём от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Принцип оптимальности означает, что для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; вместо всего кратчайшего пути &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt; достаточно хранить его последнее ребро &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt;. Отсюда следует оценка объёма памяти &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Описание входных и выходных данных ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&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;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Преобразование выходных данных и поиск кратчайшего пути ===&lt;br /&gt;
&lt;br /&gt;
Кратчайший путь от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; может быть восстановлен за время &amp;lt;math&amp;gt;O(\left |P^*(u, v) \right |)&amp;lt;/math&amp;gt;, если, начиная с вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, проходить рёбра &amp;lt;math&amp;gt;e^*_w&amp;lt;/math&amp;gt; в обратном направлении до тех пор, пока не будет посещена вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рёбра &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt; могут быть восстановлены за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; перебором рёбер для каждой вершины:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
    e^*_v \in \{ (w, v) \in E \mid f^*(v) = f^*(w) + f((v, w)) \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Перебор может осуществляться параллельно.&lt;br /&gt;
&lt;br /&gt;
Для поиска кратчайших расстояний &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; по набору рёбер &amp;lt;math&amp;gt;e^*_v&amp;lt;/math&amp;gt;, необходимо построить из них дерево, на котором выполнить [[Поиск в ширину (BFS)|поиск в ширину]] за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (с возможностью параллелизации).&lt;br /&gt;
&lt;br /&gt;
== Алгоритмы решения задачи ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[[Поиск в ширину (BFS)]]&#039;&#039;&#039; для ориентированных невзвешенных графов, сложность &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Дейкстры]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1, 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Беллмана-Форда]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; для ориентированных графов с произвольными весами, сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Δ-шагания]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Meyer, U, and P Sanders. “Δ-Stepping: a Parallelizable Shortest Path Algorithm.” Journal of Algorithms 49, no. 1 (October 2003): 114–52. doi:10.1016/S0196-6774(03)00076-2.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, средняя сложность на графах со случайными весами &amp;lt;math&amp;gt;O(n + m + dL)&amp;lt;/math&amp;gt;, параллельная сложность &amp;lt;math&amp;gt;O((dL + \ln n) \ln n&amp;lt;/math&amp;gt; со средней работой &amp;lt;math&amp;gt;O(n + m + dL \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Алгоритм Торупа&#039;&#039;&#039;&amp;lt;ref name=thorup /&amp;gt; представляет собой теоретическое доказательство возможности решения задачи для неориентированных графов с положительными целыми весами за линейное время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В ориентированном ациклическом графе с произвольными весами время поиска кратчайших путей линейное – &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;, алгоритм основан на топологической сортировке графа.&lt;br /&gt;
&lt;br /&gt;
Обозначения: &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; – число рёбер, &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; – число вершин, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; – максимальная степень вершины, &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; – максимальный суммарный вес кратчайшего пути.&lt;br /&gt;
&lt;br /&gt;
Перечисленные алгоритмы могут использоваться и для решения [[Поиск кратчайшего пути для всех пар вершин (APSP)|задачи поиска кратчайшего пути для всех пар вершин графа]] (для этого необходимо запустить соответствующий алгоритм для каждой вершины графа, что при необходимости можно сделать параллельно), при этом оценка сложности умножается на &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
&lt;br /&gt;
[[En: Single Source Shortest Path (SSSP)]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%BB%D0%B0%D1%81%D1%81%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2&amp;diff=25609</id>
		<title>Классификация алгоритмов</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%BB%D0%B0%D1%81%D1%81%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2&amp;diff=25609"/>
		<updated>2018-06-14T11:48:09Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__TOC__&lt;br /&gt;
= Задачи алгебры =&lt;br /&gt;
==Матричные и векторные операции==&lt;br /&gt;
===Векторные операции===&lt;br /&gt;
# {{level|Суммирование сдваиванием}}&lt;br /&gt;
## {{level|Нахождение суммы элементов массива сдваиванием}}&lt;br /&gt;
## {{level|Нахождение частных сумм элементов массива сдваиванием}}&lt;br /&gt;
# {{level|Равномерная норма вектора, вещественная версия, последовательно-параллельный вариант}}&lt;br /&gt;
# {{level|Скалярное произведение векторов, вещественная версия, последовательно-параллельный вариант}}&lt;br /&gt;
# {{level|Последовательно-параллельный метод суммирования}}&lt;br /&gt;
=== Умножения матриц на вектор ===&lt;br /&gt;
==== Умножения неособенных матриц на вектор ====&lt;br /&gt;
# {{level|Умножение плотной неособенной матрицы на вектор (последовательный вещественный вариант)}}&lt;br /&gt;
==== Умножения матриц специального вида на вектор ====&lt;br /&gt;
# {{level|Преобразование Фурье}}&lt;br /&gt;
## {{level|Быстрое преобразование Фурье для составной размерности}}&lt;br /&gt;
### {{level|Быстрое преобразование Фурье для степеней двойки}}&lt;br /&gt;
#### {{level|Простой алгоритм Кули-Тьюки быстрого преобразования Фурье для степеней двойки}}&lt;br /&gt;
#### {{level|Быстрое преобразование Фурье для чётных степеней двойки}}&lt;br /&gt;
### {{level|Быстрое преобразование Фурье для составной размерности с небольшими простыми делителями (2,3,5,7)}}&lt;br /&gt;
## {{level|Быстрое преобразование Фурье для простой размерности}}&lt;br /&gt;
===Матричные операции===&lt;br /&gt;
# {{level|Умножение плотных матриц}}&lt;br /&gt;
## {{level|Перемножение плотных неособенных матриц (последовательный вещественный вариант)}}&lt;br /&gt;
## {{level|Метод Штрассена}}&lt;br /&gt;
==Разложения матриц==&lt;br /&gt;
{{level|Задача разложения матриц}}&lt;br /&gt;
# {{level|Треугольные разложения}}&lt;br /&gt;
## {{level|Метод Гаусса (нахождение LU-разложения)}}&lt;br /&gt;
### {{level|LU-разложение методом Гаусса без перестановок}}&lt;br /&gt;
#### {{level|LU-разложение методом Гаусса}}&lt;br /&gt;
#### {{level|Компактная схема метода Гаусса и её модификации}}&lt;br /&gt;
##### {{level|Компактная схема метода Гаусса для плотной матрицы}}&lt;br /&gt;
##### {{level|Компактная схема метода Гаусса для трёхдиагональной матрицы и её модификации}}&lt;br /&gt;
###### {{level|Компактная схема метода Гаусса для трёхдиагональной матрицы, последовательный вариант}}&lt;br /&gt;
###### {{level|Алгоритм сдваивания Стоуна для LU-разложения трёхдиагональной матрицы}}&lt;br /&gt;
###### {{level|Последовательно-параллельный алгоритм для LU-разложения трёхдиагональной матрицы}}&lt;br /&gt;
### {{level|LU-разложение методом Гаусса с перестановками}}&lt;br /&gt;
#### {{level|LU-разложение методом Гаусса с выбором ведущего элемента по столбцу}}&lt;br /&gt;
#### {{level|LU-разложение методом Гаусса с выбором ведущего элемента по строке}}&lt;br /&gt;
#### {{level|LU-разложение методом Гаусса с выбором ведущего элемента по главной диагонали}}&lt;br /&gt;
#### {{level|LU-разложение методом Гаусса с выбором ведущего элемента по всей матрице}}&lt;br /&gt;
## {{level|Метод Холецкого (нахождение симметричного треугольного разложения)}}&lt;br /&gt;
### {{level|Разложение Холецкого (метод квадратного корня)}} базовый точечный вещественный вариант для плотной симметричной положительно-определённой матрицы&lt;br /&gt;
# {{level|Известные треугольные разложения для матриц специального вида}}&lt;br /&gt;
# {{level|Унитарно-треугольные разложения}}&lt;br /&gt;
## {{level|QR-разложения плотных неособенных матриц}}&lt;br /&gt;
### {{level|Метод Гивенса (вращений) QR-разложения матрицы}}&lt;br /&gt;
#### {{level|Метод Гивенса (вращений) QR-разложения квадратной матрицы (вещественный точечный вариант)}}&lt;br /&gt;
### {{level|Метод Хаусхолдера (отражений) QR-разложения матрицы}}&lt;br /&gt;
#### {{level|Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный точечный вариант}}&lt;br /&gt;
### {{level|Метод ортогонализации}}&lt;br /&gt;
#### {{level|Классический метод ортогонализации}}&lt;br /&gt;
#### {{level|Метод ортогонализации с переортогонализацией}}&lt;br /&gt;
### {{level|Метод треугольного разложения матрицы Грама}}&lt;br /&gt;
## {{level|Методы QR-разложения плотных хессенберговых матриц}}&lt;br /&gt;
### {{level|Метод Гивенса (вращений) QR-разложения хессенберговой матрицы (вещественный вариант)}}&lt;br /&gt;
### {{level|Метод Хаусхолдера (отражений) QR-разложения хессенберговой матрицы (вещественный вариант)}}&lt;br /&gt;
# {{level|Разложения, содержащие матрицу, подобную исходной}}&lt;br /&gt;
## {{level|Разложения, содержащие хессенбергову матрицу, унитарно подобную исходной}}&lt;br /&gt;
### {{level|Метод Хаусхолдера (отражений) приведения матрицы к хессенберговой (почти треугольной) форме}}&lt;br /&gt;
#### {{level|Классический точечный метод Хаусхолдера (отражений) приведения матрицы к хессенберговой (почти треугольной) форме}}&lt;br /&gt;
### {{level|Метод Гивенса (вращений) приведения матрицы к хессенберговой (почти треугольной) форме}}&lt;br /&gt;
#### {{level|Классический точечный метод Гивенса (вращений) приведения матрицы к хессенберговой (почти треугольной) форме}}&lt;br /&gt;
## {{level|Разложения, содержащие трёхдиагональную матрицу, унитарно подобную исходной}}&lt;br /&gt;
### {{level|Метод Хаусхолдера (отражений) приведения к трёхдиагональному виду}}&lt;br /&gt;
#### {{level|Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду}}&lt;br /&gt;
#### {{level|Метод Хаусхолдера (отражений) для приведения комплексных эрмитовых матриц к трёхдиагональному симметричному виду}}&lt;br /&gt;
### {{level|Метод Гивенса (вращений) приведения матрицы к трёхдиагональной форме}}&lt;br /&gt;
## {{level|Спектральное разложение (нахождение собственных значений и векторов)}}&lt;br /&gt;
# {{level|Разложения с использованием унитарных преобразований, не содержащие матриц, подобной исходной}}&lt;br /&gt;
## {{level|Разложения на две унитарные и одну двухдиагональную матрицы}}&lt;br /&gt;
### {{level|Метод Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме}}&lt;br /&gt;
### {{level|Метод Гивенса (вращений) приведения матрицы к двухдиагональной форме}}&lt;br /&gt;
## {{level|Разложения на две унитарные и одну диагональную матрицы}}&lt;br /&gt;
### {{level|Сингулярное разложение (нахождение сингулярных значений и векторов)}}&lt;br /&gt;
#### {{level|Методы нахождения сингулярных чисел двухдиагональных матриц}}&lt;br /&gt;
##### {{level|Алгоритм dqds нахождения сингулярных чисел двухдиагональной матрицы}}&lt;br /&gt;
###### {{level|Итерация алгоритма dqds}}&lt;br /&gt;
&lt;br /&gt;
==Решение систем линейных уравнений==&lt;br /&gt;
# {{level|Прямые методы решения СЛАУ}}&lt;br /&gt;
## {{level|Linpack benchmark}}&lt;br /&gt;
## {{level|Методы решения СЛАУ с матрицами специального вида}}&lt;br /&gt;
### {{level|Методы решения СЛАУ с треугольными матрицами}}&lt;br /&gt;
#### {{level|Прямая подстановка (вещественный вариант)}}&lt;br /&gt;
#### {{level|Обратная подстановка (вещественный вариант)}}&lt;br /&gt;
#### {{level|Методы решения СЛАУ с двудиагональными матрицами}}&lt;br /&gt;
##### {{level|Прямая и обратная подстановка в СЛАУ с двухдиагональной матрицей}}&lt;br /&gt;
##### {{level|Метод сдваивания Стоуна для решения двудиагональных СЛАУ}}&lt;br /&gt;
##### {{level|Последовательно-параллельный вариант обратной подстановки}}&lt;br /&gt;
### {{level|Методы решения СЛАУ с трёхдиагональными матрицами}}&lt;br /&gt;
#### {{level|Методы, основанные на стандартном LU-разложении матрицы}}&lt;br /&gt;
##### {{level|Прогонка}}&lt;br /&gt;
###### {{level|Прогонка, точечный вариант}}&lt;br /&gt;
###### {{level|Классическая монотонная прогонка, повторный вариант}}&lt;br /&gt;
##### {{level|Метод сдваивания Стоуна}}&lt;br /&gt;
###### {{level|Алгоритм сдваивания Стоуна для LU-разложения трёхдиагональной матрицы}}&lt;br /&gt;
###### {{level|Метод сдваивания Стоуна для решения двудиагональных СЛАУ}}&lt;br /&gt;
##### {{level|Последовательно-параллельный вариант решения трёхдиагональной СЛАУ с LU-разложением и обратными подстановками}}&lt;br /&gt;
#### Другие методы&lt;br /&gt;
##### {{level|Метод редукции}}&lt;br /&gt;
###### {{level|Полный метод редукции}}&lt;br /&gt;
###### {{level|Повторный метод редукции для новой правой части}}&lt;br /&gt;
##### {{level|Встречная прогонка}}&lt;br /&gt;
###### {{level|Встречная прогонка, точечный вариант}}&lt;br /&gt;
###### {{level|Повторная встречная прогонка, точечный вариант}}&lt;br /&gt;
##### {{level|Метод циклической редукции}}&lt;br /&gt;
###### {{level|Полный метод циклической редукции}}&lt;br /&gt;
###### {{level|Повторный метод циклической редукции для новой правой части}}&lt;br /&gt;
##### {{level|Метод окаймления}}&lt;br /&gt;
### Методы решения СЛАУ с блочно-треугольными матрицами&lt;br /&gt;
#### {{level|Блочная прямая подстановка (вещественный вариант)|Блочная прямая подстановка}}&lt;br /&gt;
#### {{level|Блочная обратная подстановка (вещественный вариант)|Блочная обратная подстановка}}&lt;br /&gt;
#### Методы решения СЛАУ с блочно-двухдиагональными матрицами&lt;br /&gt;
##### {{level|Прямая и обратная подстановка в СЛАУ с блочно-двухдиагональной матрицей}}&lt;br /&gt;
##### {{level|Метод сдваивания Стоуна для решения блочно-двухдиагональных СЛАУ}}&lt;br /&gt;
##### {{level|Блочный последовательно-параллельный вариант обратной подстановки для решения блочно-двухдиагональных СЛАУ}}&lt;br /&gt;
### {{level|Методы решения СЛАУ с блочно-трёхдиагональными матрицами}}&lt;br /&gt;
#### Методы, основанные на стандартном LU-разложении матрицы&lt;br /&gt;
##### {{level|Блочная прогонка}}&lt;br /&gt;
##### {{level|Блочный последовательно-параллельный вариант решения с LU-разложением и обратными подстановками}}&lt;br /&gt;
#### Другие методы&lt;br /&gt;
##### {{level|Встречная прогонка, блочный вариант}}&lt;br /&gt;
##### {{level|Блочный метод циклической редукции}}&lt;br /&gt;
##### {{level|Блочный метод окаймления}}&lt;br /&gt;
## {{level|Решения СЛАУ с матрицами специального вида, имеющими известные обратные матрицы}}&lt;br /&gt;
# Итерационные методы решения СЛАУ&lt;br /&gt;
## {{level|High Performance Conjugate Gradient (HPCG) benchmark}}&lt;br /&gt;
## {{level|Стабилизированный метод бисопряженных градиентов (BiCGStab)}}&lt;br /&gt;
## {{level|Алгоритм_Качмажа}}&lt;br /&gt;
&lt;br /&gt;
==Решения спектральных задач==&lt;br /&gt;
# {{level|Спектральное разложение (нахождение собственных значений и векторов)}}&lt;br /&gt;
## {{level|QR-алгоритм}}&lt;br /&gt;
### {{level|QR-алгоритм, используемый в SCALAPACK}}&lt;br /&gt;
#### {{level|Классический точечный метод Хаусхолдера (отражений) приведения матрицы к хессенберговой (почти треугольной) форме}}&lt;br /&gt;
#### {{level|QR-алгоритм для хессенберговой матрицы, используемый в SCALAPACK}}&lt;br /&gt;
### {{level|QR-алгоритм для симметричных матриц, используемый в SCALAPACK}}&lt;br /&gt;
#### {{level|Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду}}&lt;br /&gt;
#### {{level|QR-алгоритм для симметричных трёхдиагональных матриц, используемый в SCALAPACK}}&lt;br /&gt;
### {{level|QR-алгоритм для комплексных эрмитовых матриц, используемый в SCALAPACK}}&lt;br /&gt;
#### {{level|Метод Хаусхолдера (отражений) для приведения комплексных эрмитовых матриц к трёхдиагональному симметричному виду}}&lt;br /&gt;
#### {{level|QR-алгоритм для симметричных трёхдиагональных матриц, используемый в SCALAPACK}}&lt;br /&gt;
## {{level|Метод Якоби (вращений) для решения спектральной задачи у симметричных матриц}}&lt;br /&gt;
### {{level|Классический метод Якоби (вращений) для симметричных матриц с выбором по всей матрице}}&lt;br /&gt;
### {{level|Метод Якоби (вращений) для симметричных матриц с циклическим исключением}}&lt;br /&gt;
### {{level|Метод Якоби (вращений) для симметричных матриц с циклическим исключением и барьерами}}&lt;br /&gt;
## {{level|Метод Ланцоша}}&lt;br /&gt;
### {{level|Алгоритм Ланцоша для точной арифметики (без переортогонализации)}}&lt;br /&gt;
# {{level|Частичная спектральная задача}}&lt;br /&gt;
## {{level|Метод бисекций}}&lt;br /&gt;
# {{level|Сингулярное разложение (нахождение сингулярных значений и векторов)}}&lt;br /&gt;
## {{level|Метод Якоби (вращений) для нахождения сингулярных значений неособенных матриц}}&lt;br /&gt;
### {{level|Метод Якоби (вращений) для нахождения сингулярных значений с циклическим перебором}}&lt;br /&gt;
### {{level|Метод Якоби для нахождения сингулярных значений со специальным подбором вращений}}&lt;br /&gt;
## {{level|QR-алгоритм в приложении к сингулярному разложению}}&lt;br /&gt;
==Алгебра многочленов==&lt;br /&gt;
# {{level|Схема Горнера, вещественная версия, последовательный вариант}}&lt;br /&gt;
&lt;br /&gt;
= Алгоритмы на списках и массивах =&lt;br /&gt;
==Алгоритмы поиска==&lt;br /&gt;
# {{level|Линейный поиск - находит элемент в любом списке|Линейный поиск}}, &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
# {{level|Двоичный поиск - находит элемент в отсортированном списке|Двоичный поиск}}, &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt;&lt;br /&gt;
==Алгоритмы сортировки==&lt;br /&gt;
# {{level|Сортировка с помощью двоичного дерева}}&lt;br /&gt;
# {{level|Сортировка пузырьком}}&lt;br /&gt;
# {{level|Сортировка слиянием (последовательный и параллельный варианты)}}&lt;br /&gt;
==Алгоритмы на графах==&lt;br /&gt;
# Обход графа&lt;br /&gt;
## {{level|Поиск в ширину (BFS)}}&lt;br /&gt;
## {{level|Поиск в глубину (DFS)}}&lt;br /&gt;
# {{level|Поиск кратчайшего пути от одной вершины (SSSP)}} {{Buttonlink|http://top53.parallel.ru/algo_results/task/29}}&lt;br /&gt;
## {{level|Поиск в ширину (BFS)}} (для невзвешенных графов)&lt;br /&gt;
## {{level|Алгоритм Дейкстры}} {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/5}}&lt;br /&gt;
## {{level|Алгоритм Беллмана-Форда}} {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/6}}&lt;br /&gt;
## {{level|Алгоритм Δ-шагания}} {{Buttonlink|http://top53.parallel.ru/algo_results/algorithm/2}}&lt;br /&gt;
# {{level|Поиск кратчайшего пути для всех пар вершин (APSP)}} &lt;br /&gt;
## {{level|Алгоритм Джонсона}}&lt;br /&gt;
## {{level|Алгоритм Флойда-Уоршелла}}&lt;br /&gt;
# {{level|Поиск транзитивного замыкания орграфа}}&lt;br /&gt;
## {{level|Алгоритм Пурдома}}&lt;br /&gt;
# {{level|Определение диаметра графа}}&lt;br /&gt;
# {{level|Построение минимального остовного дерева (MST)}}&lt;br /&gt;
## {{level|Алгоритм Борувки}}&lt;br /&gt;
## {{level|Алгоритм Крускала}}&lt;br /&gt;
## {{level|Алгоритм Прима}}&lt;br /&gt;
## {{level|Алгоритм GHS}}&lt;br /&gt;
# {{level|Поиск изоморфных подграфов}}&lt;br /&gt;
## {{level|Алгоритм Ульмана}}&lt;br /&gt;
## {{level|Алгоритм VF2}}&lt;br /&gt;
# {{level|Связность в графах}}&lt;br /&gt;
## {{level|Алгоритм Шилоаха-Вишкина поиска компонент связности}}&lt;br /&gt;
## {{level|Система непересекающихся множеств}}&lt;br /&gt;
## {{level|Алгоритм Тарьяна поиска компонент сильной связности}}&lt;br /&gt;
## {{level|Алгоритм DCSC поиска компонент сильной связности}}&lt;br /&gt;
## {{level|Алгоритм Тарьяна поиска компонент двусвязности}}&lt;br /&gt;
## {{level|Алгоритм Тарьяна-Вишкина поиска компонент двусвязности}}&lt;br /&gt;
## {{level|Алгоритм Тарьяна поиска «мостов» в графе}}&lt;br /&gt;
## {{level|Определение вершинной связности графа}}&lt;br /&gt;
## {{level|Алгоритм Габова определения рёберной связности графа}}&lt;br /&gt;
# {{level|Поиск максимального потока в транспортной сети}}&lt;br /&gt;
## {{level|Алгоритм Форда-Фалкерсона}}&lt;br /&gt;
## {{level|Алгоритм проталкивания предпотока}}&lt;br /&gt;
# {{level|Поиск потока минимальной стоимости в транспортной сети}}&lt;br /&gt;
# {{level|Задача о назначениях}}&lt;br /&gt;
## {{level|Венгерский алгоритм}}&lt;br /&gt;
## {{level|Алгоритм аукциона}}&lt;br /&gt;
## {{level|Алгоритм Гопкрофта-Карпа}}&lt;br /&gt;
# {{level|Вычисление betweenness centrality|Вычисление центральности вершин}}&lt;br /&gt;
=Вычислительная геометрия=&lt;br /&gt;
# {{level|Поиск диаметра множества точек}}&lt;br /&gt;
# {{level|Построение выпуклой оболочки набора точек}}&lt;br /&gt;
# {{level|Триангуляция Делоне}}&lt;br /&gt;
# {{level|Диаграмма Вороного}}&lt;br /&gt;
# {{level|Принадлежность точки многоугольнику}}&lt;br /&gt;
# {{level|Пересечения выпуклых многоугольников}} &lt;br /&gt;
# {{level|Пересечение звёздных многоугольников}}&lt;br /&gt;
==Компьютерная графика==&lt;br /&gt;
# {{level|Алгоритмы построения отрезка - алгоритмы для аппроксимации отрезка на дискретной графической поверхности}}&lt;br /&gt;
# {{level|Алгоритм определения видимых частей трёхмерной сцены}}&lt;br /&gt;
# {{level|Трассировка лучей - рендеринг реалистичных изображений}}&lt;br /&gt;
# {{level|Глобальное освещение - рассматривает прямое освещение и отражение от других объектов}}&lt;br /&gt;
&lt;br /&gt;
=Исследование и моделирование компьютеров=&lt;br /&gt;
==Тесты производительности компьютеров==&lt;br /&gt;
# {{level|High Performance Conjugate Gradient (HPCG) benchmark}}&lt;br /&gt;
# {{level|Linpack benchmark}}&lt;br /&gt;
==Алгоритмы моделирования квантовых систем==&lt;br /&gt;
# {{level|Алгоритмы моделирования квантовых вычислений}}&lt;br /&gt;
## {{level|Однокубитное преобразование вектора-состояния}}&lt;br /&gt;
## {{level|Двухкубитное преобразование вектора-состояния}}&lt;br /&gt;
## {{level|Моделирование квантового преобразования Фурье}}&lt;br /&gt;
==Алгоритмы машинного обучения==&lt;br /&gt;
# {{level|Алгоритм k средних (k-means)}}&lt;br /&gt;
===Нейронные сети===&lt;br /&gt;
# {{level|Распознавание образов}}&lt;br /&gt;
## {{level|Распознавание текста}}&lt;br /&gt;
## {{level|Распознавание речи}}&lt;br /&gt;
## {{level|Распознавание лиц}}&lt;br /&gt;
===Алгоритмы теории игр===&lt;br /&gt;
&lt;br /&gt;
= Прикладные задачи из разных областей =&lt;br /&gt;
==Алгоритмы оптимизации==&lt;br /&gt;
# {{level|Линейное программирование}}&lt;br /&gt;
# {{level|Симплекс-метод}}&lt;br /&gt;
# {{level|Метод ветвей и границ}}&lt;br /&gt;
# {{level|Генетические алгоритмы}}&lt;br /&gt;
# {{level|Муравьиные алгоритмы}}&lt;br /&gt;
# {{level|Комбинированные алгоритмы}}&lt;br /&gt;
# {{level|Стохастическое двойственное динамическое программирование (SDDP)}}&lt;br /&gt;
==Решение систем нелинейных уравнений==&lt;br /&gt;
# {{level|Метод Ньютона для систем нелинейных уравнений}}&lt;br /&gt;
==Численные методы интегрирования==&lt;br /&gt;
# {{level|Квадратурные формулы}}&lt;br /&gt;
# {{level|Квадратурные (кубатурные) методы численного интегрирования по отрезку (многомерному кубу)}}&lt;br /&gt;
## [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Метод прямоугольников|Метод прямоугольников]]&lt;br /&gt;
## [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Метод трапеций|Метод трапеций]]&lt;br /&gt;
## [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Метод парабол (метод Симпсона)|Метод парабол (метод Симпсона)]]&lt;br /&gt;
## [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Метод Гаусса|Метод Гаусса]]&lt;br /&gt;
==Алгоритмы решения уравнений математической физики==&lt;br /&gt;
# {{level|Уравнение Пуассона, решение дискретным преобразованием Фурье}}&lt;br /&gt;
=Криптографические алгоритмы=&lt;br /&gt;
# {{level|Метод встречи посередине}}&lt;br /&gt;
=Другие алгоритмы=&lt;br /&gt;
&lt;br /&gt;
[[en:Algorithm classification]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы|*]]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Buttonlink&amp;diff=25608</id>
		<title>Шаблон:Buttonlink</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Buttonlink&amp;diff=25608"/>
		<updated>2018-06-14T11:41:25Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[{{{1}}} &amp;lt;span style=&amp;quot;font-size:10px;&amp;quot; class=&amp;quot;mw-ui-button mw-ui-progressive&amp;quot;&amp;gt;Получить данные&amp;lt;/span&amp;gt;]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Buttonlink&amp;diff=25607</id>
		<title>Шаблон:Buttonlink</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Buttonlink&amp;diff=25607"/>
		<updated>2018-06-14T11:32:34Z</updated>

		<summary type="html">&lt;p&gt;Apaokin: Новая страница: «[{{{1}}} &amp;lt;span class=&amp;quot;mw-ui-button mw-ui-progressive&amp;quot;&amp;gt;Получить данные&amp;lt;/span&amp;gt;]»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[{{{1}}} &amp;lt;span class=&amp;quot;mw-ui-button mw-ui-progressive&amp;quot;&amp;gt;Получить данные&amp;lt;/span&amp;gt;]&lt;/div&gt;</summary>
		<author><name>Apaokin</name></author>
	</entry>
</feed>