<?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>ProGrammer &#187; Алгоритмы</title>
	<atom:link href="/?feed=rss2&#038;cat=26" rel="self" type="application/rss+xml" />
	<link></link>
	<description>Сайт о программировании, математике и моделировании</description>
	<lastBuildDate>Sat, 21 Jan 2012 17:31:04 +0000</lastBuildDate>
	<language>ru</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.4</generator>
		<item>
		<title>Сложение длинных чисел</title>
		<link>/?p=208</link>
		<comments>/?p=208#comments</comments>
		<pubDate>Fri, 03 Dec 2010 20:07:01 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Алгоритмы]]></category>
		<category><![CDATA[Задачи и решения]]></category>
		<category><![CDATA[ассемблер]]></category>
		<category><![CDATA[длинная арифметика]]></category>

		<guid isPermaLink="false">http://s12.localhost/?p=208</guid>
		<description><![CDATA[За операцию сложения отвечает процедура ADD DIGIT Аdd( DIGIT C[ ], // результат const DIGIT A[ ], // первое слагаемое const DIGIT B[ ], // второе слагаемое int n) // длина слагаемых {              TWODIGIT T; DIGIT d=0; int i; for(i=0; i&#60;n; i++) {              T = (TWODIGIT)A[i]+B[i]+d; C[i] = LODIGIT(T); d = HIDIGIT(T); } return d;]]></description>
			<content:encoded><![CDATA[<p>За операцию сложения отвечает процедура ADD</p>
<p><strong>DIGIT А</strong><strong>dd(</strong></p>
<p><strong>DIGIT </strong><strong>C[ ], // результат </strong></p>
<p><strong>const </strong><strong>DIGIT </strong><strong>A[ ], // первое слагаемое </strong></p>
<p><strong>const </strong><strong>DIGIT </strong><strong>B[ ], // второе слагаемое </strong></p>
<p><strong>int </strong><strong>n) // длина слагаемых <span id="more-208"></span></strong></p>
<p><strong>{              TWODIGIT T;</strong></p>
<p><strong> DIGIT d=0;</strong></p>
<p><strong> int i;</strong></p>
<p><strong> for(i=0; i&lt;n; i++)</strong></p>
<p><strong> {              T = (TWODIGIT)A[i]+B[i]+d;</strong></p>
<p><strong> C[i] = LODIGIT(T);</strong></p>
<p><strong> d = HIDIGIT(T);</strong></p>
<p><strong> }</strong></p>
<p><strong> </strong><strong>return </strong><strong>d;</strong></p>
<p><strong>}</strong></p>
<p style="text-align: justify;">Сложность данного алгоритма O(n). Реализация этого алгоритма проста, так как он имитирует известную всем процедуру сложения в «столбик».</p>
]]></content:encoded>
			<wfw:commentRss>/?feed=rss2&#038;p=208</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Умножение длинных чисел</title>
		<link>/?p=195</link>
		<comments>/?p=195#comments</comments>
		<pubDate>Fri, 03 Dec 2010 19:50:02 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Алгоритмы]]></category>
		<category><![CDATA[Информатика и программирование]]></category>
		<category><![CDATA[Математика]]></category>
		<category><![CDATA[алгоритм]]></category>
		<category><![CDATA[длинная арифметика]]></category>

		<guid isPermaLink="false">http://s12.localhost/?p=195</guid>
		<description><![CDATA[Умножение в столбик. С умножением дело обстоит не так просто, как с операциями сложения и вычитания. Дело в том, что известный нам со школы алгоритм умножения “в столбик” не самый быстрый. Вот он. Алгоритм 1.3. Умножение «в столбик» длинных чисел С = А*В длины n цифр 1.       C := 0; (достаточно обнулить n младших цифр)]]></description>
			<content:encoded><![CDATA[<p><strong>Умножение в столбик.</strong></p>
<p style="text-align: justify;">С умножением дело обстоит не так просто, как с операциями сложения и вычитания. Дело в том, что известный нам со школы алгоритм умножения “в столбик” не самый быстрый. Вот он.</p>
<p><strong>Алгоритм 1.3. Умножение «в столбик» длинных чисел С = А*В<span id="more-195"></span></strong></p>
<p><strong>длины </strong><strong>n</strong><strong> цифр</strong></p>
<p>1.       C := 0; (достаточно обнулить n младших цифр)</p>
<p>2.       для i = 0 … n-1 выполнить шаги 3-8;</p>
<p>3.       d :=0;</p>
<p>4.       для j = 0 … n-1 выполнить шаги 3-5;</p>
<p>5.       T :=C<sub>i+j </sub>+ A<sub>i</sub>*B<sub>i </sub>+d;</p>
<p>6.       C<sub>i+j </sub>:<sub> </sub>= LODIGIT(T);</p>
<p>7.       d := HIDIGIT(T);</p>
<p>8.       C<sub>i+n</sub> := d;</p>
<p>9.       конец.</p>
<p style="text-align: justify;">Обратите внимание на то, что результат операции умножения в общем случае в 2 раза длиннее сомножителей, то есть имеет длину 2n цифр. Нетрудно видеть, что описанный алгоритм требует выполнения n операций умножения и еще некоторого количества операций сложения. И именно так умножали люди с давних времен, пока 39 лет назад московским математиком А.А. Карацубой не было совершено неожиданное открытие. Он придумал куда более эффективный метод.</p>
<p><strong>Метод Карацубы</strong></p>
<p>Пусть А и В — два n-значных длинных числа и n &#8211; четное. Эти числа можно представить в виде</p>
<p>А = b<sup>n</sup><sup>/2</sup>U<sub>1 </sub>+ U<sub>0</sub>, B = b<sup>n</sup><sup>/2</sup>V<sub>1 </sub>+ V<sub>0</sub>,</p>
<p>где U<sub>0</sub>,<sub> </sub>U<sub>1</sub>,V<sub>0</sub>,V<sub>1</sub> — n-значные числа. Обычное умножение “в столбик” равносильно следующему:</p>
<p>АЧВ = (b<sup>n</sup><sup>/2</sup>U<sub>1 </sub>+ U<sub>0</sub>)(b<sup>n</sup><sup>/2</sup>V<sub>1 </sub>+ V<sub>0</sub> ) = b<sup>n</sup>U<sub>1</sub>V<sub>1</sub>+ b<sup>n</sup><sup>/2 </sup>(U<sub>1</sub>V<sub>0 </sub>+ U<sub>0</sub>V<sub>1</sub>) + U<sub>0</sub>V<sub>0</sub>,</p>
<p>те. требует выполнения 4 операций умножения -значных чисел.</p>
<p>Однако, воспользовавшись тождеством</p>
<p>(U<sub>1 </sub>- U<sub>0</sub>) (V<sub>0 </sub>-V<sub>1</sub>) = -U<sub>1</sub>V<sub>1</sub>-U<sub>0</sub>V<sub>0</sub>+ U<sub>1</sub>V<sub>0</sub>+U<sub>0</sub>V<sub>1</sub>,</p>
<p>получаем</p>
<p>АЧВ=(b<sup>n</sup><sup>/2</sup>U<sub>1</sub>+U<sub>0</sub>)(b<sup>n</sup><sup>/2</sup>V<sub>1</sub>+V<sub>0</sub> )=(b<sup>n</sup>+ b<sup>n</sup><sup>/2</sup>)U<sub>1</sub>V<sub>1</sub>+ b<sup>n</sup><sup>/2</sup>(U<sub>1 </sub>- U<sub>0</sub>)(V<sub>0 </sub>-V<sub>1</sub>)+(b<sup>n</sup><sup>/2</sup>+1)U<sub>0</sub>V<sub>0</sub>.</p>
<p style="text-align: justify;">При этом умножение n-значных чисел потребует выполнения З операций умножения-значных чисел и еще некоторое количество операций сложения и вычитания. Итак, налицо выигрыш  по сравнению с умножением в столбик. Но поскольку и умножение -значных чисел тоже можно выполнять таким же образом, организован рекурсивный вызов процедуры умножения и т.д., то теоретически получим сложность около n<sup>1.6</sup> элементарных умножений, а не n<sup>2</sup>, как раньше.</p>
<p style="text-align: justify;">На практике не все столь радужно, как показалось. При малых значениях n скорее всего можно проиграть на накладных расходах (организация рекурсии и циклов, дополнительные операции вычитания, трудности с учетом знака результата и т.п.). Практические вычисления показали, что реальный выигрыш наступает при разрядности операндов где-то около 16384 битов, что не может интересовать, поскольку ГОСТ Р 34.10-94 регламентирует длину модуля не более 1024, да и в реальной практике пока модулярные вычисления с разрядностью выше 2048 битов используются редко. А по сему реализацию этого метода рассматривать здесь не будем.</p>
<p style="text-align: justify;">Интересующимся же заметим, что метод Карацубы был в 1963 году обобщен А. Тоомом, который нашел связь этой задачи с интерполяционными полиномами. И еще более продвинулись в решении задачи умножения А. Шенхаге и Ф. Штрассен, которые помимо интерполяционных полиномов использовали быстрое преобразование Фурье и построили алгоритм умножения с числом элементарных операций умножения не более О(nЧlog nЧlog log n). Однако, как и метод Карацубы, эти алгоритмы слишком сложны в реализации и дают практический выигрыш только при очень большой разрядности сомножителей.</p>
<p><strong>Умножение на цифру</strong></p>
<p style="text-align: justify;">Далее при делении понадобится функция умножения «длинного» n-значного числа на цифру. В принципе, можно поступить просто и обратиться к алгоритму умножения, предварительно обобщив его на операнды различной длины. Поступим по-другому</p>
<p><strong>Алгоритм 1.4. Умножения числа А длины </strong><strong>n</strong><strong> цифр на цифру х:</strong></p>
<p><strong>С = А*х</strong></p>
<p>1.        Т:=0;</p>
<p>2.        для i = 0.. .n-1 выполнить 3-4;</p>
<p>3.        Т:=Т/b+А<sub>i</sub> Ч х;</p>
<p>4.        С<sub>i</sub><sub> </sub>:= LODIGIT(Т);</p>
<p>5.        Сn := HIDIGIT(T);</p>
<p>б.       конец.</p>
<p>Кстати, при этом получается число С длины n+ 1. Даже, если старшая цифра результата равна нулю, требуем, чтобы для нее была выделена память.</p>
]]></content:encoded>
			<wfw:commentRss>/?feed=rss2&#038;p=195</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Разработка программы реализующей алгоритм Рабина-Миллера на С++</title>
		<link>/?p=119</link>
		<comments>/?p=119#comments</comments>
		<pubDate>Wed, 01 Dec 2010 20:46:59 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Алгоритмы]]></category>
		<category><![CDATA[алгоритм]]></category>
		<category><![CDATA[С++]]></category>
		<category><![CDATA[тест Рабина-Миллера]]></category>

		<guid isPermaLink="false">http://s12.localhost/?p=119</guid>
		<description><![CDATA[При выборе языка написания программы главными критериями являлись скорость выполнения и трудоемкость написания. С++ является одним из наиболее распространенных современных языков программирования. Язык С++ хорошо зарекомендовал себя эффективностью, лаконичностью записи алгоритмов, логической стойкостью программ. Ключевым понятием С++ является класс. Класс &#8211; это определяемый пользователем тип. Классы обеспечивают упрятывание данных, их инициализацию, неявное преобразование пользовательских типов,]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">При выборе языка написания программы главными критериями являлись скорость выполнения и трудоемкость написания. С++ является одним из наиболее распространенных современных языков программирования. Язык С++ хорошо зарекомендовал себя эффективностью, лаконичностью записи алгоритмов, логической стойкостью программ. Ключевым понятием С++ является класс. Класс &#8211; это определяемый пользователем тип. Классы обеспечивают упрятывание данных, их инициализацию, неявное преобразование пользовательских типов, динамическое задание типов, контролируемое пользователем управление памятью и средства для перегрузки операций. В языке С++ концепции контроля типов и модульного построения программ реализованы более полно, чем в С. Кроме того, С++ содержит усовершенствования, прямо с классами не связанные: символические константы, функции-подстановки, стандартные значения параметров функций, перегрузка имен функций, операции управления свободной памятью и ссылочный тип. В С++ сохранены все возможности С эффективной работы с основными объектами, отражающими аппаратную &laquo;реальность&raquo; (разряды, байты, слова, адреса и т.д.). Это позволяет достаточно эффективно реализовывать пользовательские типы.<span id="more-119"></span></p>
<p style="text-align: justify;">При реализации алгоритма Рабина-Миллера был создан класс длинной арифметики long_ar, где в качестве функций были встроены операции сложения длинных чисел, вычитание длинных чисел, умножение длинных чисел на число, перемножение двух длинных чисел, поиск остатка от деления двух длинных чисел. Также в класс long_ar вошли такие функции, как обнуление длинных чисел, печать длинных чисел, создание длинных чисел при чтении из файла, определение количества битов в длинном числе, проверка делимости длинного числа на три и на два, сравнение с нулем, единицей и с 100000, проверка нечетности, побитовый сдвиг вправо с присвоением и др.</p>
<p>Программы состоит из двух подпрограмм: is_probably_prime() и is_sprp(int n, int b) &#8211; и начинается с вызова is_probably_prime().</p>
<p><strong>Подпрограмма is_probably_prime()</strong></p>
<p>Шаг 1: ввести проверяемое число num.</p>
<p>Шаг 2: если num &#8211; четное, то вывести сообщение, что число составное, т.к. единственное простое четное число &#8211; это 2, а программа рассчитана на проверку длинных чисел. Завершить программу.</p>
<p>Шаг 3: если число num делится на три &#8211; вывести сообщение, что число делится на три (неделимость на три &#8211; необходимое условие для теста Рабина-Миллера). Завершить программу.</p>
<p>Шаг 4: ввести i &gt; 0 (количество прохождений теста Рабина-Миллера). Если тест даст результат, что num &#8211; простое, то от величины i будет зависеть погрешность полученного результата.</p>
<p>Шаг 5: сгенерировать случайное нечетное число base, 1 &lt; base &lt; num.</p>
<p>Шаг 6: запустить подпрограмму is_sprp со входными параметрами (num, base).</p>
<p>Шаг 7: если is_sprp возвращает значение false &#8211; вывести сообщение, что число составное и завершить программу. Иначе уменьшить i на единицу.</p>
<p>Шаг 8: если i больше нуля перейти к шагу 5.</p>
<p>Шаг 9: Вывести сообщение, что число простое и завершить программу.</p>
<p><strong>Подпрограмма</strong><strong> is_sprp(int n, int b)</strong></p>
<p>Шаг 1: num = n.</p>
<p>Шаг 2: s = 1. Тип s &#8211; integer.</p>
<p>Шаг 3: e = num &gt;&gt; 1. e &#8211; вспомогательная переменная. Операция &laquo;&gt;&gt;&raquo; обозначает побитовый сдвиг вправо.</p>
<p>Шаг 4: пока e четно, выполнять побитовый сдвиг e с присвоением   (e &gt;&gt;= 1) и инкапсуляцию s (s++). Как только e станет нечетно &#8211; переход к шагу 5.</p>
<p>Шаг 5: factor = 1, congr = b (и factor и congr &#8211; длинные числа).</p>
<p>Шаг 6: если e меньше двух &#8211; переход к шагу 11.</p>
<p>Шаг 7: если e нечетно, factor = (factor * congr)%num.</p>
<p>Шаг 8: congr = (congr * congr)%num.</p>
<p>Шаг 9: e &gt;&gt;= 1 (побитовый сдвиг с присвоением).</p>
<p>Шаг 10: переход к шагу 6.</p>
<p>Шаг 11: congr = (congr * factor)%num.</p>
<p>Шаг 12: если congr == 1, вернуть значение true.</p>
<p>Шаг 13: r = 0.</p>
<p>Шаг 14: если выполняется условие (congr == num &#8211; 1), вернуть значение true.</p>
<p>Шаг 15: congr = (congr * congr)%num.</p>
<p>Шаг 16: увеличить r на единицу.</p>
<p>Шаг 17: если r &lt; s перейти к шагу 14.</p>
<p>Шаг 18: вернуть значение false.</p>
]]></content:encoded>
			<wfw:commentRss>/?feed=rss2&#038;p=119</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Объединение тестов проверки на простоту</title>
		<link>/?p=117</link>
		<comments>/?p=117#comments</comments>
		<pubDate>Wed, 01 Dec 2010 20:43:45 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Алгоритмы]]></category>
		<category><![CDATA[алгоритм]]></category>
		<category><![CDATA[тест Рабина-Миллера]]></category>

		<guid isPermaLink="false">http://s12.localhost/?p=117</guid>
		<description><![CDATA[Первым возможным улучшением предложенного теста является использование для небольших целых n &#62; 1 в качестве оснований теста Рабина-Миллера последовательных простых чисел больших или равных 2. К примеру, доказаны следующие утверждения : Если n &#60; 2·1012 и является 2, 3, 5, 7 и 11-SPRP, то оно простое. Если n &#60; 3·1014 и является 2, 3, 5,]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Первым возможным улучшением предложенного теста является использование для небольших целых n &gt; 1 в качестве оснований теста Рабина-Миллера последовательных простых чисел больших или равных 2. К примеру, доказаны следующие утверждения :<span id="more-117"></span></p>
<ul>
<li>Если n &lt; 2·10<sup>12</sup> и является 2, 3, 5, 7 и 11-SPRP, то оно простое.</li>
<li>Если n &lt; 3·10<sup>14</sup> и является 2, 3, 5, 7, 11 и 13-SPRP, то оно простое.</li>
<li>Если n &lt; 3,4·10<sup>14</sup> и является 2, 3, 5, 7, 11, 13 и 17-SPRP, то оно простое.</li>
</ul>
<p style="text-align: justify;">Следует обратить внимание на то, что эти тесты не вероятностные, они доказывают простоту числа. Логичным завершением рассмотрения такого подхода является утверждение, предложенное Миллером:</p>
<p style="text-align: justify;"><strong>Тест Миллера:</strong> если верна расширенная гипотеза Римана, то если n является a-SPRP для всех a: 1 &lt; a &lt; 2 (log n)<sup>2</sup>, то n – простое.</p>
<p style="text-align: justify;">Сама гипотеза Римана слишком сложна, чтобы ее здесь приводить, но если она верна, то мы получим полиномиальный алгоритм, проверяющий является ли n простым числом. На практике тест Миллера не используется, так как пока не доказана расширенная гипотеза Римана.</p>
<p style="text-align: justify;">Если рассматривать по отдельности, то все приведенные ранее проверки на простоту работают либо недостаточно надежно, либо недостаточно быстро. Вместе с тем, если объединить тест Рабина-Миллера с пробным делением, мы получаем алгоритм, работающий намного лучше и того и другого. Дело в том, что для большого n вычислительно проще провести пробное деление на небольшое простое число, а не тест Рабина-Миллера. При этом сразу же отбрасывается достаточно большая часть составных чисел. К примеру, при проверке делимости на 3, 5 и 7 отбрасывается ~50% всех составных нечетных чисел, при проверке делимости на все простые меньшие 256 отбрасывается уже ~80% всех составных нечетных чисел.</p>
<p><strong>Объединенный алгоритм (для k-битного числа):</strong></p>
<p style="text-align: justify;">1. Пробное деление на все числа меньше некоторого граничного числа B. Это число определяется временем полного возведения в степень по модулю k-битного числа и полным временем проверки делимости k-битного числа на небольшое простое число и равно примерно их отношению.</p>
<p style="text-align: justify;">2. Тест Рабина-Миллера, проводимый t раз, где t выбирается из соображений необходимой точности.</p>
<p style="text-align: justify;">Можно подобрать такие значения B и t можно получить существенно лучшие результаты, чем при использовании просто теста Рабина-Миллера. К примеру, для k = 500 и t = 6 вероятность ошибки такого алгоритма ~2<sup>-92</sup>, что существенно меньше, чем 2<sup>-2</sup><sup>t</sup> = 2<sup>-12</sup>. Объединенный алгоритм (тест Рабина-Миллера + пробное деление) находит широкое применение в криптосистемах с открытым ключом для построения простых ключей длиной 512, 1024 и 2048 бит.</p>
]]></content:encoded>
			<wfw:commentRss>/?feed=rss2&#038;p=117</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Методы проверки на простоту</title>
		<link>/?p=114</link>
		<comments>/?p=114#comments</comments>
		<pubDate>Wed, 01 Dec 2010 20:41:13 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Алгоритмы]]></category>
		<category><![CDATA[Математика]]></category>
		<category><![CDATA[вероятностные тесты]]></category>
		<category><![CDATA[метод пробных делений]]></category>
		<category><![CDATA[метод Ферма]]></category>
		<category><![CDATA[решето Эрастофена]]></category>
		<category><![CDATA[тест Леманна]]></category>
		<category><![CDATA[тест Рабина-Миллера]]></category>

		<guid isPermaLink="false">http://s12.localhost/?p=114</guid>
		<description><![CDATA[Все алгоритмы проверки простоты делятся на две больших подгруппы: детерминированные и вероятностные проверки. Алгоритмы первой группы позволяют точно сказать, является число простым или составным. Алгоритмы второй группы позволяют это определить, но с некоторой вероятностью ошибки. Многократное их повторение для одного числа, но с разными параметрами, обычно позволяет сделать вероятность ошибки сколь угодно малой величиной. Метод]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Все алгоритмы проверки простоты делятся на две больших подгруппы: детерминированные и вероятностные проверки. Алгоритмы первой группы позволяют точно сказать, является число простым или составным. Алгоритмы второй группы позволяют это определить, но с некоторой вероятностью ошибки. Многократное их повторение для одного числа, но с разными параметрами, обычно позволяет сделать вероятность ошибки сколь угодно малой величиной.<span id="more-114"></span></p>
<p><strong>Метод пробных делений</strong><strong> </strong></p>
<p style="text-align: justify;">Этот метод относится к группе детерминированных методов. Пусть <em>n Є </em>N. Как проверить, является ли <em>n </em>простым? Пока не существовало необходимости генерировать большие простые числа, можно было использовать методы проверки, которые достаточно легко реализуемы без применения вычислительной техники и не требуют больших усилий для проверки маленьких чисел. Первым из таких методов является, естественно, полный перебор всех возможных делителей. Чаще всего используют модификацию такого перебора, называемую пробным делением (trial division): чтобы проверить число на простоту, делим его на все простые меньше либо равные корню из этого числа. Если n—составное, то n =ab, где , причем . Поэтому для d = 2, 3, . . ., [] следует проверить, делится ли n на d? Если делитель числа n не будет найден, то n—простое. В противном случае будет найден минимальный простой делитель числа n, т.е. мы даже разложим n на два множителя. Сложность метода составляет C*n<sup>1/2</sup> арифметических операций с целыми числами.</p>
<p style="text-align: justify;">В одиночку метод пробных делений не используется из-за большой вычислительной сложности. Пробное деление на маленькие простые числа используется как один из шагов во многих тестах.</p>
<p style="text-align: justify;">Возможны модификации этого метода, которые работают быстрее. Например, мы можем проверить, делится ли n на 2 и на 3 , и если нет, то перебираем далее только числа d вида 1 + 6j и 5+ 6j, j =1, 2, &#8230; Сложность метода отличается от предыдущей лишь постоянной в C<sub>1</sub>(·)</p>
<p><strong> </strong></p>
<p><strong>Решето Эратосфена</strong></p>
<p style="text-align: justify;">Для построения простых чисел, меньших какому-либо N, можно воспользоваться так называемым решетом Эратосфена. Если мы хотим составить таблицу всех простых чисел среди чисел 2, 3, <em>. . . </em>, <em>N</em>, то нужно сперва вычеркнуть все числа, делящиеся на 2, кроме 2. Затем взять число 3 и вычеркнуть все последующие числа, делящиеся на3 . Затем взять следующее не вычеркнутое число (т. е. 5) и вычеркнуть все последующие делящиеся на него числа, и так далее. В итоге останутся лишь простые числа. Для реализации метода нужен большой объем памяти ЭВМ, однако для составления таблиц простых чисел он является наилучшим. В книге  описаны эффективные алгоритмы, реализующие решето Эратосфена для построения таблиц простых чисел и вычисление факторных баз.  Этот метод также как и метод пробных делений является детерминированным.</p>
<p><strong> </strong></p>
<p><strong>Вероятностные тесты</strong></p>
<p style="text-align: justify;">Пусть n Є N, n нечетно, n &gt; 1. Вероятностный тест на простоту проводится следующим образом. Выбирается случайное a Є N, 1 ≤ a &lt;n, и для него проверяется выполнение некоторых условий. Если какое-то из условий не выполнено, то число n—составное, поскольку для простых чисел эти условия являются необходимыми. Если же все условия выполнены, то из этого еще не следует простота n. Однако можно будет считать, что «n —простое число с некоторой вероятностью». Кроме того, обычно доказывают оценку снизу для этой вероятности. Чем больше значений a мы протестируем, тем ближе эта вероятность к единице.</p>
<p><strong>Малая теорема Ферма</strong></p>
<p style="text-align: justify;">Для проверки простоты чисел n порядка 10<sup>30</sup>—10<sup>40</sup> метод пробных делений уже неприменим, потому что даже на самых мощных компьютерах вычисления займут много времени (несколько лет). В 17 веке французский математик Пьер Ферма выдвинул утверждение, которое лежит в основе практически всех возможных тестов на простоту. Малая теорема Ферма: Если n &#8211; простое и a – любое целое, то . В частности, если n не делит a, то .</p>
<p style="text-align: justify;"><a href="/wp-content/uploads/2010/12/128.png"><img class="aligncenter size-full wp-image-115" title="1" src="/wp-content/uploads/2010/12/128.png" alt="" width="141" height="28" /></a></p>
<p>На основании этой теоремы можно построить эффективный тест на простоту.</p>
<p style="text-align: justify;"><strong>Тест Ферма</strong>: для n &gt; 1 выбираем a &gt; 1 и проверяем малую теорему Ферма. Если малая теорема Ферма не выполнена, то n &#8211; составное. Если же она выполнена, то мы еще не можем сделать вывод о простоте n, поскольку теорема дает лишь необходимое условие. Этот тест является эффективным для обнаружения составных чисел.</p>
<p><strong>Тест Рабина-Миллера и сильно возможно простые числа.</strong></p>
<p style="text-align: justify;">Можно существенно улучшить тест Ферма, заметив, что если n – простое нечетное, то для 1 есть только два квадратных корня по модулю n: 1 и -1. Таким образом, квадратный корень из a<sup>n-1</sup>, a<sup>(n-1)/2 </sup>равен плюс или минус единице. Если (n – 1)/2 опять нечетно, то мы сможем снова извлечь корень и так далее. Первый вариант алгоритма, предлагает использовать только одно деление:</p>
<p style="text-align: justify;"><strong>Тест Леманна</strong> (Lehmann): если для какого-либо целого числа a меньшего n не выполняется условие a<sup>(n-1)/2</sup> ±1 (mod n), то число n – составное. Если это условие выполняется, то число n – возможно простое, причем вероятность ошибки не превышает 50%. Тест Леманна не используется, так как он хуже аналогичного вероятностного теста Рабина-Миллера. Но этот тест можно естественным образом улучшить, если извлекать корень по модулю не один раз, а столько, сколько получится.</p>
]]></content:encoded>
			<wfw:commentRss>/?feed=rss2&#038;p=114</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Оценка ускорения РО-алгоритма</title>
		<link>/?p=102</link>
		<comments>/?p=102#comments</comments>
		<pubDate>Wed, 01 Dec 2010 20:26:24 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Алгоритмы]]></category>
		<category><![CDATA[Тестирование программ]]></category>
		<category><![CDATA[алгоритм]]></category>
		<category><![CDATA[метод Брента]]></category>
		<category><![CDATA[программная модель]]></category>
		<category><![CDATA[РО-алгоритм Полларда]]></category>
		<category><![CDATA[сложность алгоритма]]></category>
		<category><![CDATA[эксперимент]]></category>

		<guid isPermaLink="false">http://s12.localhost/?p=102</guid>
		<description><![CDATA[Для оценки скорости работы и ускорения алгоритма построенному с помощью различных методов была разработана программа, реализующая данные методы и проведены экспериментальные исследования. Измерения производились на Celeron 900MH, 256Mb оперативной памяти, ОС Windows XP. Самым быстрым оказался алгоритм построенный на методе Брента. Он смог работать  более быстро с большими числами, чем остальные алгоритмы. Классический метод Полларда]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Для оценки скорости работы и ускорения алгоритма построенному с помощью различных методов была разработана программа, реализующая данные методы и проведены экспериментальные исследования. Измерения производились на Celeron 900MH, 256Mb оперативной памяти, ОС Windows XP.<span id="more-102"></span></p>
<p style="text-align: center;"><a href="/wp-content/uploads/2010/12/126.png"><img class="aligncenter size-full wp-image-103" title="Результаты работы программы." src="/wp-content/uploads/2010/12/126.png" alt="" width="415" height="470" /></a></p>
<p style="text-align: justify;">Самым быстрым оказался алгоритм построенный на методе Брента. Он смог работать  более быстро с большими числами, чем остальные алгоритмы. Классический метод Полларда перестал работать с числами порядка 10<sup>31</sup>. Метод ускорения с помощью китайской теоремы об остатках работает с большими числами, но медленнее чем метод ускорения методом Брента.</p>
<p style="text-align: justify;">Анализируя результаты выполнения алгоритмов можно сделать вывод что самым быстрым алгоритмом стал алгоритм Полларда ускоренный с помощью алгоритма нахождения периода последовательности методом Брента. Самым медленным – классический алгоритм Полларда. Алгоритм ускоренный с помощью китайской теоремы об остатках оказался более быстрым чем классический, но более медленным, чем алгоритм Полларда ускоренный с помощью алгоритма нахождения периода последовательности методом Брента.</p>
]]></content:encoded>
			<wfw:commentRss>/?feed=rss2&#038;p=102</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ускорение РО-алгоритма с помощью алгоритма нахождения периода последовательности методом Брента.</title>
		<link>/?p=100</link>
		<comments>/?p=100#comments</comments>
		<pubDate>Wed, 01 Dec 2010 20:20:49 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Алгоритмы]]></category>
		<category><![CDATA[алгоритм]]></category>
		<category><![CDATA[метод Брента]]></category>
		<category><![CDATA[наибольший общий делитель]]></category>
		<category><![CDATA[РО-алгоритм Полларда]]></category>

		<guid isPermaLink="false">http://s12.localhost/?p=100</guid>
		<description><![CDATA[1. [Начальная установка.] Присвоить x←5, x’←2, k←1, l←1, n←N. (Во время выполнения этого алгоритма число n не является множителем числа N, а переменные x и  x’ представляют величины xm mod n xl(m)-1 mod n в выражении (1), где также f(x)=x2+1, A=2, l=l(m) и k=2l-m.) 2.1. [Проверить, будет ли число простым.] Если n – простое число]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">1. [Начальная установка.] Присвоить x←5, x’←2, k←1, l←1, n←N. (Во время выполнения этого алгоритма число n не является множителем числа N, а переменные x и  x’ представляют величины x<sub>m</sub> mod n x<em><sub>l</sub></em><em><sub>(</sub></em><em><sub>m</sub></em><em><sub>)-</sub></em><sub>1 </sub>mod n в выражении (1), где также f(x)=x<sup>2</sup>+1, A=2, l=<em>l</em><em>(</em><em>m</em><em>)</em> и k=2l-m.)</p>
<p style="text-align: justify;">2.1. [Проверить, будет ли число простым.] Если n – простое число<span id="more-100"></span></p>
<p style="text-align: justify;">2.2. Вывести n в качестве результата; на этом выполнение алгоритма завершается.</p>
<p style="text-align: justify;">3.1. [Множитель найден?] Присвоить g←gcd(x’-x, n).</p>
<p style="text-align: justify;">3.2. Если g=1, то перейти к шагу 4;</p>
<p style="text-align: justify;">3.3. В противном случае вывести g.</p>
<p style="text-align: justify;">3.4. Если g=n, алгоритм завершается (его выполнение прерывается, ибо нам известно, что n не является простым числом).</p>
<p style="text-align: justify;">3.5. В противном случае присвоить n ← n/g, x ← x mod n, x’ ← x’ mod n и возвратится к шагу 2. (Заметим, что g может и не быть простым числом – это подлежит проверке. В каждом случае, когда g – не простое число, его простые множители не могут быть определены при помощи этого алгоритма.)</p>
<p style="text-align: justify;">4.1. [Продвинуться.] Присвоить k ← k – 1.</p>
<p style="text-align: justify;">4.2. Если k=0,</p>
<p style="text-align: justify;">4.3. То присвоить x’ ← x, l ← 2l, k ← l. Присвоить x ← (x<sup>2</sup>+1) mod n и возвратится к шагу 3.</p>
]]></content:encoded>
			<wfw:commentRss>/?feed=rss2&#038;p=100</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ускорение РО-алгоритма</title>
		<link>/?p=98</link>
		<comments>/?p=98#comments</comments>
		<pubDate>Wed, 01 Dec 2010 20:19:28 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Алгоритмы]]></category>
		<category><![CDATA[алгоритм]]></category>
		<category><![CDATA[РО-алгоритм Полларда]]></category>

		<guid isPermaLink="false">http://s12.localhost/?p=98</guid>
		<description><![CDATA[Время на каждой итерации алгоритма Полларда, в основном, затрачивается на выпол­нение умножения и деления с многократной точностью и на вычисление наибольшего общего делителя. Выполнение этих операций может быть ускорено за счет применения методики «умножения Монтгомери». Более того, в случае, когда операция нахождения наибольшего общего делителя выполняется медленно, Поллард предложил ускорить процесс путем накапливания произведения по]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Время на каждой итерации алгоритма Полларда, в основном, затрачивается на выпол­нение умножения и деления с многократной точностью и на вычисление наибольшего общего делителя. Выполнение этих операций может быть ускорено за счет применения методики «умножения Монтгомери».<span id="more-98"></span></p>
<p style="text-align: justify;">Более того, в случае, когда операция нахождения наибольшего общего делителя выполняется медленно, Поллард предложил ускорить процесс путем накапливания произведения по модулю<em> </em><em>n</em><em>, </em>скажем, десяти последовательных значений (<em>х&#8217; </em><em>- х</em>)<em> </em>перед тем, как искать наибольший общий делитель. Таким образом, приблизительно 90% операций нахождения наибольшего общего делителя заменяется одним умножением по моду­лю <em>N</em> ценой некоторого увеличения вероятности того, что решение при этом не будет найдено.</p>
<p style="text-align: justify;">В тех редких случаях, когда для больших <em>N</em> результат не был найден, можно применить функцию <em>f</em><em>(</em><em>x</em><em>) = х<sup>2</sup> +с</em> при некотором <em>с</em> ≠ 0 или 1,  Следует избегать также значения <em>с </em><em>=</em> -2, поскольку рекуррентное уравнение <em>x<sub>m</sub></em><em><sub>+1 </sub></em><em>= </em><em>x<sub>m</sub></em><em><sup>2</sup></em><em> – 2</em> имеет решение в виде . Похоже, что другие значения параметра <em>с </em>не приводят к возникновению простых связей по модулю <em>р </em>и все они должны быть удовлетворительными при подходящих начальных значениях.</p>
<p><strong>Ускорение с помощью китайской теоремы об остатках</strong></p>
<p>1. (Инициализация) Вводим x<sub>0</sub>, k=0, y = 0;</p>
<p>2. Цикл от 1 до 10;</p>
<p>2.1 k = k + 1;</p>
<p>2.2 x<sub>k</sub> = P(x<sub>k</sub><sub>-1</sub>);</p>
<p>2.3. Если k – нечетное, идти в 2.1;</p>
<p>2.4. j = k/2;</p>
<p>2.5. m<sub>i</sub> = |x<sub>k</sub> – x<sub>j</sub>|</p>
<p>2.6. y = y + m<sub>i</sub>;</p>
<p>3 Найти НОД (n, y);</p>
<p>4. Если НОД = 1, идти в 2.1;</p>
<p>5.1 Если НОД &lt; n,</p>
<p>5.2 То p = НОД, конец;</p>
<p>5.3 Вывод p;</p>
<p>6. (Если НОД делится не только на p, но и на n). Идти в 1.</p>
]]></content:encoded>
			<wfw:commentRss>/?feed=rss2&#038;p=98</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Реализация РО-алгоритма</title>
		<link>/?p=96</link>
		<comments>/?p=96#comments</comments>
		<pubDate>Wed, 01 Dec 2010 20:17:44 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Алгоритмы]]></category>
		<category><![CDATA[алгоритм]]></category>
		<category><![CDATA[РО-алгоритм Полларда]]></category>

		<guid isPermaLink="false">http://s12.localhost/?p=96</guid>
		<description><![CDATA[РО-алгоритм Полларда 1. (Инициализация) Вводим x0, k=0; 2. k = k + 1; 3. xk = P(xk-1); 4. Если k – нечетное, идти в 2; 5.1 j = k/2. 5.2 Найти НОД (n, &#124;xk – xj&#124;); 6. Если НОД = 1, идти в 2; 7.1 Если НОД &#60; n, 7.2 То p = НОД, конец;]]></description>
			<content:encoded><![CDATA[<p><strong>РО-алгоритм Полларда</strong></p>
<p>1. (Инициализация) Вводим x<sub>0</sub>, k=0;</p>
<p>2. k = k + 1;</p>
<p>3. x<sub>k</sub> = P(x<sub>k</sub><sub>-1</sub>);</p>
<p>4. Если k – нечетное, идти в 2;</p>
<p>5.1 j = k/2.</p>
<p>5.2 Найти НОД (n, |x<sub>k</sub> – x<sub>j</sub>|);</p>
<p>6. Если НОД = 1, идти в 2;</p>
<p>7.1 Если НОД &lt; n,</p>
<p>7.2 То p = НОД, конец;</p>
<p>7.3 Вывод p;</p>
<p>8. Если НОД делится не только на p, но и на n. Идти в 1.</p>
]]></content:encoded>
			<wfw:commentRss>/?feed=rss2&#038;p=96</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Методы ускорения алгоритмов</title>
		<link>/?p=90</link>
		<comments>/?p=90#comments</comments>
		<pubDate>Wed, 01 Dec 2010 20:11:21 +0000</pubDate>
		<dc:creator>root</dc:creator>
				<category><![CDATA[Алгоритмы]]></category>
		<category><![CDATA[алгоритм]]></category>
		<category><![CDATA[метод Брента]]></category>
		<category><![CDATA[наибольший общий делитель]]></category>

		<guid isPermaLink="false">http://s12.localhost/?p=90</guid>
		<description><![CDATA[Первый метод ускорения заключается в том, что время вычисления НОД занимает много времени, поэтому используем китайскую теорему об остатках и попробуем ускорить начальный алгоритм. Китайская теорема об остатках: любое не отрицательное число которое не превышает каждого из множителей  модуля можно однозначно восстановить если известны его остатки по этим модулям. Доказательство Пусть числа и определены согласно m1m2]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Первый метод ускорения заключается в том, что время вычисления НОД занимает много времени, поэтому используем китайскую теорему об остатках и попробуем ускорить начальный алгоритм. Китайская теорема об остатках: любое не отрицательное число которое не превышает каждого из множителей  модуля можно однозначно восстановить если известны его остатки по этим модулям.<span id="more-90"></span></p>
<p style="text-align: justify;"><strong>Доказательство</strong> Пусть числа <em>и</em> определены согласно<em> m</em>1<em>m</em>2<sup> </sup>× × × <em>mk </em>= <em>Msms</em>, причем  º 1(mod <em>m</em>s),  <em>s</em> = 1, 2, . . . , <em>k</em> и целые числа  попарно взаимнопростые, то есть ,</p>
<p style="text-align: justify;">Пусть <em>b</em><sub>1</sub>, <em>b</em><sub>2</sub>, . . . , <em>b<sub>k</sub></em> тоже целые числа такие что ,</p>
<p style="text-align: justify;">Тогда система сравнений:</p>
<p style="text-align: justify;"><em>x</em> º <em>b</em>1(mod <em>m</em>1),</p>
<p style="text-align: justify;"><em>x</em> º <em>b</em>2(mod <em>m</em>2),</p>
<p style="text-align: justify;">. . . . . . . . . . .</p>
<p style="text-align: justify;"><em>x</em> º <em>bk</em>(mod <em>mk</em>)</p>
<p style="text-align: justify;">Имеет в интервале<em> </em>где M = m<sub>1</sub>×m<sub>2</sub> × × × m<sub>k</sub> одно общее решение</p>
<p style="text-align: justify;">(7)</p>
<p style="text-align: justify;">Если числа  <em>и </em> определены из условий<em> m</em>1<em>m</em>2<sup> </sup>× × × <em>mk </em>= <em>Msms</em>, причем  º 1(mod <em>m</em>s),  <em>s</em> = 1, 2, . . . , <em>k</em> и целые числа  попарно взаимнопростые, то есть , то совокупность значений <em>x</em><em> </em>которые удовлетворяют систему (1) определяется сравнением  где  +, <em>х</em> – в виде уравнения (7) удовлетворяет систему (6). Согласно другой теореме – если <em>b</em>1, <em>b</em>2, . . . , <em>bk</em> независимо друг от друга пробегают полную систему остатков по модулям . Число <em>x</em><sub>0</sub> может принять значение одного из остатков по модулю <em>M</em> =<em> </em><em>m</em><sub>1</sub>×<em>m</em><sub>2</sub> × × × <em>m<sub>k</sub></em>., т.е. .</p>
<p style="text-align: justify;">Докажем что для <em>x</em><sub>0</sub> из интервала <em> </em>для разных наборов целых чисел <em>b</em><sub>1</sub>, <em>b</em><sub>2</sub>, . . . , <em>b<sub>k</sub></em> таких что , , общее решение (7) является единственным. Доказательство будет вестись от обратного допустим что существует еще один набор чисел  таких, что ,  Пускай для конкретности эти наборы отличаются числами что стоят на <em>s</em>-позиции т.е. . Но тогда мы приходим к тому что справедливы сравнения  и  так как по условию теоремы при всех  выходит . Поэтому из равенства чисел вытекает справедливость сравнения . При условии  это значит  а это противоречит начальному условию. Следовательно решение (6) единственно.</p>
<p style="text-align: justify;">Второй метод ускорения строится на алгоритме нахождения периода последовательности методом Брента. Пусть F – конечное множество, x F и f: F → F отображение, которому соответствует последовательность x<sub>n</sub><sub>+1</sub> = f(x<sub>n</sub>), начинающая с x<sub>0</sub>.Цели алгоритма Брента – сосчитать длину периода последовательности (x<sub>n</sub>)<sub>n</sub><sub>≥0</sub>, использую при этом как можно меньше памяти . Сложность алгоритма имеет порядок квадратичного корня из F.</p>
<p style="text-align: justify;">Отметим, что для h = 2<sup>i</sup> – 1 интервал соответствующих значений k равен [h+1, 2h +1]. Для того, чтобы теперь x<sub>h</sub> = x<sub>k</sub>, необходимо и достаточно, чтобы h ≥ μ (индекс вхождения в период) и k – h было кратно λ (длина периода) – условие, которое выполняется при достаточно большом h (так как интервал для k удваивается на каждом этапе). Пусть h – какой-либо индекс, для каждого существует k  [h+1,2h+1] c x<sub>h</sub> = x<sub>k</sub>. Если k – первый индекс интервала [h+1,2h+1], для которого x<sub>h</sub> = x<sub>k</sub>, то k – h равно длине периода λ. Наконец, если μ – наименьший индекс, такой, что x<sub>μ</sub> = x<sub>μ</sub><sub>+</sub><sub>λ</sub>, то μ – индекс вхождения в период. Это приводит к алгоритму, в котором переменная j  принимает значения h+1, т.е. степени 2. Хотя алгоритм находит только длину периода, дальнейшее применение метода Брента позволяет определить все 4 параметра (h,k,μ,λ), описанные выше: сложность измеряется при помощи параметра k, который равен числу итераций x ← f(x) и обозначается k<sub>f</sub><sub>,</sub><sub>x</sub><sub>0</sub>.</p>
]]></content:encoded>
			<wfw:commentRss>/?feed=rss2&#038;p=90</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>