<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Записки программиста &#187; Алгоритмы</title>
	<atom:link href="http://arti.kz/category/algorithms/feed" rel="self" type="application/rss+xml" />
	<link>http://arti.kz</link>
	<description></description>
	<lastBuildDate>Fri, 17 Jun 2011 03:56:34 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Об одном алгоритме поиска простых чисел</title>
		<link>http://arti.kz/555-%d0%be%d0%b1-%d0%be%d0%b4%d0%bd%d0%be%d0%bc-%d0%b0%d0%bb%d0%b3%d0%be%d1%80%d0%b8%d1%82%d0%bc%d0%b5-%d0%bf%d0%be%d0%b8%d1%81%d0%ba%d0%b0-%d0%bf%d1%80%d0%be%d1%81%d1%82%d1%8b%d1%85-%d1%87%d0%b8%d1%81</link>
		<comments>http://arti.kz/555-%d0%be%d0%b1-%d0%be%d0%b4%d0%bd%d0%be%d0%bc-%d0%b0%d0%bb%d0%b3%d0%be%d1%80%d0%b8%d1%82%d0%bc%d0%b5-%d0%bf%d0%be%d0%b8%d1%81%d0%ba%d0%b0-%d0%bf%d1%80%d0%be%d1%81%d1%82%d1%8b%d1%85-%d1%87%d0%b8%d1%81#comments</comments>
		<pubDate>Sat, 10 Jan 2009 14:44:03 +0000</pubDate>
		<dc:creator>arti</dc:creator>
				<category><![CDATA[Алгоритмы]]></category>

		<guid isPermaLink="false">http://arti.kz/?p=555</guid>
		<description><![CDATA[Как-то, ради интереса, я решил поискать в Сети материалы о нахождении всех простых чисел в диапазоне от 1 до N за линейное время. Нашлись некоторые статьи на английском, но описанные алгоритмы были несколько сложны для реализации. К сожалению, поиски на русском не увенчались успехом &#8211; были какие-то оптимизации, пытающиеся уменьшить объем памяти, константу в оценке [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://ru.wikipedia.org/wiki/Эратосфен"><img class="alignright size-full wp-image-598" title="Эратосфен" src="http://arti.kz/wp-content/uploads/2009/01/eratosthenes.jpg" alt="Эратосфен" width="200" height="279" /></a><em>Как-то, ради интереса, я решил поискать в Сети материалы о нахождении всех <a title="Простое число" href="http://ru.wikipedia.org/wiki/Простое_число">простых чисел</a> в диапазоне от 1 до N за линейное время. Нашлись некоторые статьи на английском, но описанные алгоритмы были несколько сложны для реализации. К сожалению, поиски на русском не увенчались успехом &#8211; были какие-то оптимизации, пытающиеся уменьшить объем памяти, константу в оценке времени, но ничего за O(N), так что я решил восполнить данный пробел.</em></p>
<h3>Определение проблемы</h3>
<p>Для начала, вспомним, что существует так называемое <a title="Решето Эратосфена" href="http://ru.wikipedia.org/wiki/Решето_Эратосфена">решето Эратосфена</a>, позволяющее решать нашу задачу за O(N ln ln N):</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">bool</span> notPrime<span style="color: #008000;">&#91;</span>MAXN <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span><span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">int</span> primes<span style="color: #008000;">&#91;</span>MAXN<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">int</span> sieve<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> n<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">int</span> primesCount <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">int</span> sqrt_n <span style="color: #000080;">=</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span><span style="color: #008000;">&#41;</span><span style="color: #0000dd;">sqrt</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">double</span><span style="color: #008000;">&#41;</span>n<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> i <span style="color: #000080;">=</span> <span style="color: #0000dd;">2</span><span style="color: #008080;">;</span> i <span style="color: #000080;">&lt;=</span> n<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>i<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
		<span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>notPrime<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
			primes<span style="color: #008000;">&#91;</span>primesCount<span style="color: #000040;">++</span><span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> i<span style="color: #008080;">;</span>
			<span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>i <span style="color: #000080;">&lt;=</span> sqrt_n<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
				<span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> j <span style="color: #000080;">=</span> i <span style="color: #000040;">*</span> i<span style="color: #008080;">;</span> j <span style="color: #000080;">&lt;=</span> n<span style="color: #008080;">;</span> j <span style="color: #000040;">+</span><span style="color: #000080;">=</span> i<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
					notPrime<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> <span style="color: #0000ff;">true</span><span style="color: #008080;">;</span>
				<span style="color: #008000;">&#125;</span>
			<span style="color: #008000;">&#125;</span>
		<span style="color: #008000;">&#125;</span>
	<span style="color: #008000;">&#125;</span>
	<span style="color: #0000ff;">return</span> primesCount<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>

<p>Почему решето работает не за линейное время? Потому что одно число в нем может вычеркиваться несколько раз (во внутреннем цикле) . Если добиться, чтобы каждое число вычеркивалось не более одного раза, то, очевидно, алгоритм будет линейным.</p>
<p><span id="more-555"></span></p>
<h3>Решение</h3>
<p>Чтобы сделать это, нам понадобится заменить массив <strong>notPrime[]</strong> на массив <strong>int leastPrime[MAXN + 1]</strong>. <strong>leastPrime[x]</strong> будет хранить индекс минимального простого числа в <strong>primes[]</strong>, на которое делится <strong>x</strong>. Очевидно, что <strong>primes[leastPrime[x]] = x</strong> для простого <strong>x</strong>. Также, вместо того чтобы вычеркивать числа вида <strong>k * i</strong>, где <strong>i</strong> &#8211; простое, будем вычеркивать числа вида <strong>primes[k] * i</strong>, где <strong>i</strong> &#8211; это любое число, а <strong>primes[k] &lt;= primes[leastPrime[i]</strong>]:</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">int</span> leastPrime<span style="color: #008000;">&#91;</span>MAXN <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span><span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">int</span> primes<span style="color: #008000;">&#91;</span>MAXN<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">int</span> advancedSieve<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> n<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">int</span> primesCount <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> i <span style="color: #000080;">=</span> <span style="color: #0000dd;">2</span><span style="color: #008080;">;</span> i <span style="color: #000080;">&lt;=</span> n<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>i<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
		<span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>leastPrime<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
			primes<span style="color: #008000;">&#91;</span>primesCount<span style="color: #000040;">++</span><span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> i<span style="color: #008080;">;</span>
			leastPrime<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> primesCount<span style="color: #008080;">;</span>
		<span style="color: #008000;">&#125;</span>
		<span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> j <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> j <span style="color: #000080;">&lt;</span> leastPrime<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span> <span style="color: #000040;">++</span>j<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
			<span style="color: #0000ff;">int</span> t <span style="color: #000080;">=</span> primes<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span> <span style="color: #000040;">*</span> i<span style="color: #008080;">;</span>
			<span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>t <span style="color: #000080;">&lt;=</span> n<span style="color: #008000;">&#41;</span> leastPrime<span style="color: #008000;">&#91;</span>t<span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> j <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
			<span style="color: #0000ff;">else</span> <span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
		<span style="color: #008000;">&#125;</span>
	<span style="color: #008000;">&#125;</span>
	<span style="color: #0000ff;">return</span> primesCount<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>

<p>Так как любое составное число однозначно представимо в виде <strong>primes[k] * i</strong>, где <strong>primes[k] &lt;= primes[leastPrime[i]]</strong>, то каждое из них в цикле во внутреннем цикле будет пройдено ровно один раз. Также ровно один раз в перед внутренним циклом будет обработано каждое простое число. Таким образом, каждое из чисел обрабатывается ровно один раз и, следовательно, получаем линейный алгоритм.</p>
<h3>Заключение</h3>
<p>На практике алгоритм работает примерно в 2 раза быстрее приведенного простого варианта решета и примерно столько же, сколько и оптимизированный вариант.</p>
<p>Преимущества алгоритма:</p>
<ul>
<li>линейная оценка времени работы (против O(N ln ln N) у решета)</li>
<li>параллельно с простыми числами алгоритм вычисляет минимальный простой делитель каждого числа от 1 до N, что позволяет, если нужно, производить разложение чисел на простые множители за время, линейное от количества множителей.</li>
</ul>
<p>Недостатки алгоритма:</p>
<ul>
<li>большая константа из-за постоянного умножения во внутреннем цикле, избавится от которого пока не понятно как;</li>
<li>требования к памяти: алгоритм требует обязательного хранения списка простых чисел (в простом решете это необязательно), а также массива длины O(N), причем для каждого числа необходимо хранить количество битов, вмещающее sqrt(N) (против решета, в котором для одного числа достаточно хранить один бит).</li>
</ul>
<p>Файлы:</p>
<ul>
<li><a href="http://arti.kz/wp-content/uploads/2009/01/sieve-vanilla.cpp">простое решето</a>;</li>
<li><a href="http://arti.kz/wp-content/uploads/2009/01/sieve-optimized.cpp">оптимизированный вариант простого решета</a>;</li>
<li><a href="http://arti.kz/wp-content/uploads/2009/01/advanced-sieve-vanilla.cpp">продвинутое решето</a>;</li>
<li><a href="http://arti.kz/wp-content/uploads/2009/01/advanced-sieve-optimized.cpp">оптимизированный вариант продвинутого решета</a>.</li>
</ul>
<p><strong>P.S.</strong> Спасибо <strong>Андрею Лопатину</strong>, который рассказал об этом алгоритме на школьных сборах, где я и подслушал его.</p>
]]></content:encoded>
			<wfw:commentRss>http://arti.kz/555-%d0%be%d0%b1-%d0%be%d0%b4%d0%bd%d0%be%d0%bc-%d0%b0%d0%bb%d0%b3%d0%be%d1%80%d0%b8%d1%82%d0%bc%d0%b5-%d0%bf%d0%be%d0%b8%d1%81%d0%ba%d0%b0-%d0%bf%d1%80%d0%be%d1%81%d1%82%d1%8b%d1%85-%d1%87%d0%b8%d1%81/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>(a * b) % c</title>
		<link>http://arti.kz/18-a-b-c</link>
		<comments>http://arti.kz/18-a-b-c#comments</comments>
		<pubDate>Sat, 19 Jan 2008 18:42:31 +0000</pubDate>
		<dc:creator>arti</dc:creator>
				<category><![CDATA[Алгоритмы]]></category>

		<guid isPermaLink="false">http://arti.kz/?p=18</guid>
		<description><![CDATA[На сайте TopCoder появилась очередная статья из раздела Feature Article &#8211; Primality Testing : Non-deterministic Algorithms, в начале которой описан любопытный метод вычисления произведения a * b % c, если все числа 64-битные и длинная арифметика не используется. Метод похож на быстрое возведение в степень и работает за O(log b). Вот, собственно, код, немного оптимизированный [...]]]></description>
			<content:encoded><![CDATA[<p>На сайте <a href="http://www.topcoder.com/tc">TopCoder</a> появилась очередная статья из раздела <a href="http://www.topcoder.com/tc?module=Static&amp;d1=features&amp;d2=archive">Feature Article</a> &#8211; <a href="http://www.topcoder.com/tc?module=Static&amp;d1=tutorials&amp;d2=primalityTesting">Primality Testing : Non-deterministic Algorithms</a>,  в начале которой описан любопытный метод вычисления произведения a * b % c, если все числа 64-битные и длинная арифметика не используется. Метод похож на быстрое возведение в степень и работает за O(log b).</p>
<p>Вот, собственно, код, немного оптимизированный мною:</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">long</span> <span style="color: #0000ff;">long</span> multiply<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">long</span> <span style="color: #0000ff;">long</span> a, <span style="color: #0000ff;">long</span> <span style="color: #0000ff;">long</span> b, <span style="color: #0000ff;">long</span> <span style="color: #0000ff;">long</span> c<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
   <span style="color: #0000ff;">long</span> <span style="color: #0000ff;">long</span> x <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span>, y <span style="color: #000080;">=</span> a <span style="color: #000040;">%</span> c<span style="color: #008080;">;</span>
   b <span style="color: #000040;">%</span><span style="color: #000080;">=</span> c<span style="color: #008080;">;</span>
   <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>b <span style="color: #000080;">&gt;</span> <span style="color: #0000dd;">0</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
       <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>b <span style="color: #000040;">&amp;</span> <span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
           x <span style="color: #000040;">+</span><span style="color: #000080;">=</span> y<span style="color: #008080;">;</span>
           <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>x <span style="color: #000080;">&gt;=</span> c<span style="color: #008000;">&#41;</span> x <span style="color: #000040;">-</span><span style="color: #000080;">=</span> c<span style="color: #008080;">;</span>
       <span style="color: #008000;">&#125;</span>
       y <span style="color: #000080;">&lt;&lt;=</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
       <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>y <span style="color: #000080;">&gt;=</span> c<span style="color: #008000;">&#41;</span> y <span style="color: #000040;">-</span><span style="color: #000080;">=</span> c<span style="color: #008080;">;</span>
       b <span style="color: #000080;">&gt;&gt;=</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
   <span style="color: #008000;">&#125;</span>
   <span style="color: #0000ff;">return</span> x<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://arti.kz/18-a-b-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

