· Начало · Отвђтить · Статистика · Поиск · FAQ · Правила · Установки · Язык · Выход · WASM.RU · Noir.Ru ·

 WASM Phorum —› WASM.A&O —› Поговорим о факторизации

Посл.отвђт Сообщенiе


Дата: Авг 13, 2004 22:25:55

Как всегда, тут я буду писать пару алгоритмов из Кнута. Второй том. Получисленные алгоритмы.
Я готовлюсь ко третьей части упаковщиков, но этот материал в статью не пойдет (мало чести гордится переписанными на сишные перепевы серьезнейшими алгоритмами).

Огромное спасибо Dmit, за то, что он обратил мое внимание на сие:
http://virlib.eunnet.net/books/numbers/
Relf'у спасибо за то, что он обратил на это внимание Дмита. С этой ссылки началось мое путешествие в теорию чисел.

Рассматривается раздел второго тома - 4.5 - "Разложение на простые множители".

Алгоритм А.
Метод последовательного деления на все простые числа поочередно. Таблица простых чисел генерируется при помощи хитрого решета Эратосфена. Алгоритм немного подкручен - не рассматриваются все четные числа, т.к. все они являются составными. Память тоже отводится по-умному (сам себя не похвалишь, кто похвалит?).
Алгоритм может факторизовать числа лишь под unsigned int максимум. Т.е. не более 4294967295 - 4-х миллионов. Работает довольно быстро.
Код приводится ниже. Код на С++, но, по сути, можно считать, что на С.
Для обработки больших чисел пришлось бы реализовать арифметику бесконечной точности (раздел 4.3 того же тома).
#include <iostream>
#include <math.h>

using namespace std;

inline static unsigned pi(unsigned x)
{
	return unsigned (x / (log((double)x)-1.08366)); 
}

/**
 * Input: M - the number we are creating a sieve up to.
 * Output: allocated array of int's (EOF of the array is 0)
 *
 * Description is here:
 * http://mathworld.wolfram.com/SieveofEratosthenes.html

 * The main idea is to process the odd numbers only.
 * Created using the following algo:
 * http://okmij.org/ftp/Scheme/Eratosthenes-sieve.txt

 * for efficiency #t and #f are swapped
 * For memory saving purposes we roughly try to calculate the 
 * number of the primes in the given range - pi function.
 **
 */
static unsigned *Eratosthenes_sieve(unsigned M)
{
	char *v = 0;				// auxiliary array for storing #t or #f
	v = new char[M+1];
	unsigned i = 0;				// i - index for "v" array, 0 <= i <= M

	unsigned *primes = 0;		// we collect prime numbers here, the EOF marker is 0
	primes = new unsigned[pi(M)+1];
	primes[0] = 2;	
	
	unsigned k = 1;				// k - index for "primes" array, 0 <= k <= M

	unsigned p = 0;				// will hold the odd numbers
	unsigned j = 0;				// auxiliary variable to get rid of the multiples
	
	do
	{
		v[i] = 0;
		v[i+1] = 0;
		i += 2;
	}while(i < M);
		
	i = 0;

	do
	{
		p = 2*i + 3;
		if (p > M) break;
		if (!v[p]) // p is the prime indeed
		{
			//cout << p << endl;
			primes[k] = p;
			k++;
			// now we remove all the odd multiples
			for(j = 1; j <= M; j++)
			{
				unsigned tmp = (2*j + 1)*(2*i + 3);

				if (tmp <= M) 
					(v[tmp] = 1); 
				else
					break;
			}
		}

		i++;
	}while(i < M);
	primes[k] = 0;

	delete v;
	return primes;
}

/**
 * Simple factoring algorithm.
 * Donald Knuth. Algorithm 4.5.4A.
 **
 */
void fact_A(unsigned N)
{
	//t and p are NOT used, we are just printing out the results
	unsigned k = 0, n = N;
    unsigned *d = Eratosthenes_sieve((unsigned)ceil(sqrt((double)N+1)));
	
	do
	{
		unsigned q = n/d[k];
		unsigned r = n%d[k];
		
		if(r)
		{
			if(q > d[k])
			{
				k++;
				continue;
			}
			else
			{
				cout << n << endl;
				break;
			}
		}
		else
		{
			cout << d[k] << endl;
			n = q;
		}
	} while(n != 1);

	delete d;
}



void main()
{
	fact_A(4294967293);
}



Дата: Авг 14, 2004 01:20:24 · Поправил: volodya

Есть еще такая любопытная функция - функция Эйлера. Подробнее функция описана все по той же ссылке:
"§ 3. Важнейшие функции в теории чисел", раздел "Пункт 14. Примеры мультипликативных функций.", "Пример 4. Функция Эйлера.".

Для вычисления этой функции требуется факторизовать число (читай - привести в каноническую форму). Алгоритм fact_A вполне справляется с этой задачей. Сама функция реализуется достаточно просто (только я категорически не согласен с тем, что написано тут:

http://algolist.manual.ru/maths/count_fast/phi_n.php).

Ребята предлагают считать степени, а степени считать для вычислений - это порнография в данном случае, т.к. есть другая формула, приведенная в уже упомянутой книжке.

Прошу прощения за использование С++ - в этот раз действительно используется С++ и STL, т.к. мне было лениво писать собственную реализацию для того, чтобы получить уникальные значения простых чисел (если непонятно, то разложите число 50 на простые множители - это будет 5*5*2 - так вот, функция Эйлера требует только 5 и 2, т.е. вторая пятерка должна быть исключена).

Поэтому имеем:
double Euler_totient(unsigned x)
{
	vector <unsigned> factored;
	
	fact_A(x, factored);
	
	vector <unsigned>::iterator new_end = unique(factored.begin(), factored.end());
	factored.erase(new_end, factored.end());

	double n = x;

	for(vector <unsigned>::iterator i = factored.begin(); i != factored.end(); i++)
	{
		n *= (1 - 1.0/(*i));
	}
	
	return n;
}


Например, для 1000 такая функция даст 400. Это означает, что есть 400 простых чисел в интервале от 0 до 1000.
Также fact_A-алгоритм немного модифицирован. Теперь он принимает ссылку на вектор.


Дата: Авг 14, 2004 01:46:25

„Например, для 1000 такая функция даст 400. Это означает, что есть 400 простых чисел в интервале от 0 до 1000.“

volodya, Вы немного не правы. Ф-ция Эйлера возвращает количество натуральных чисел, меньших ее аргумента и взаимно простых с ним. Действительно, EulerPhi[1000] == 400. Но количество простых чисел в интервале [1, 1000] равно pi(1000) == 168.

P.S. Спасибо за приведенную ссылку.
P.P.S. Очень рекомендую пакет Mathematica - IMHO весьма полезный для реверсинга инструмент ;-)


Дата: Авг 14, 2004 21:38:23 · Поправил: volodya

volodya, Вы немного не правы.

Ох, да :( Спасибо, что поправил!

"Функция Эйлера phi( a ) есть количество чисел из ряда 0, 1, 2,..., a - 1, взаимно простых с a ."

Да, Mathematica, штука, конечно, хорошая, но мне нравится Maple :)


Дата: Авг 15, 2004 10:22:36

[offtop]
volodya
Maple? Честно говоря, я боюсь её использовать. В ней стоолько багов, причём таких откровенных, что зачастую приходится пересчитывать в других пакетах…


Дата: Авг 17, 2004 22:37:48 · Поправил: volodya

Ладно, продолжаем. Сегодня у нас в гостях метод Полларда (Pollard Rho method).

Линки на теоретический ликбез:
http://algolist.manual.ru/maths/teornum/factor/p-1.php
http://mathworld.wolfram.com/PollardRhoFactorizationMethod.html


На
http://algolist.manual.ru/maths/teornum/factor/
есть линк на "A Survey of Modern Integer Factorization Methods" - подберите.

Линки на понажимать кнопочки:
http://linguistlist.org/~zheng/courseware/pollard.html
http://www.louisville.edu/~dawill03/crypto/PollardRho.html

Для Поллардовского метода нам понадобятся некоторые дополнительные функции.
1) gcd - алгоритм Евклида:
http://www.wasm.ru/forum/index.php?action=vthread&forum=17&topic=6839

2) вероятностный тест Миллера-Рабина на простоту числа - взят из Кнута и дополнительно требует:
2.1) быстрое возведение в степень
2.2) представление числа в форме n - 1 = 2kq:
http://www.wasm.ru/forum/index.php?action=vthread&forum=17&topic=6841

Теперь мы готовы. Код:
static unsigned powmod(unsigned a, unsigned k, unsigned n)
{
  unsigned b=1;

  while (k) 
  {
	  if (!(k%2)) 
	  {
		  k /= 2;
		  a = (a*a)%n;
	  }
	  else 
	  {
		  k--;
		  b = (b*a)%n;
	  }
  }
  return b;
}

/* Donald Knuth, vol 2., algorithm 4.5.4P - probability test for prime*/
static bool isprime(unsigned n)
{
	srand(time(0));

	unsigned x = rand() % (n - 1);
        unsigned k, q;
	do_N(n, k, q);

	unsigned j = 0;
	unsigned y = powmod(x, q, n);
	
	while(j < k)
	{
	        if(((!j) && (y == 1)) || (y == n - 1))
			return true;
		if((j > 0) && (y == 1))
			return false;

		j++;
		y = (y*y)%n;
        }

	return false;
}

static unsigned pollard_rho(unsigned n)
{
	if(isprime(n))
		return 0;

	unsigned factor = 1, x = 5, y = 2;

	while(gcd(x-y, n) == 1)
	{
		x = (x*x + 1)%n;
		x = (x*x + 1)%n;
		y = (y*y + 1)%n;
	}
	
	factor = gcd(x-y, n);
	if(factor < n)
		return factor;

	return 0;
}


void main()
{
	cout << pollard_rho(561);
}



Дата: Авг 17, 2004 23:26:04

Еще хорошая ссылочка:

http://www.frenchfries.net/paul/factoring/theory/index.html


Дата: Авг 18, 2004 00:20:39

Метод разложения на множители Ферма лучше всего описан тут:
http://www.wasm.ru/forum/index.php?action=vthread&forum=18&topic=6661

Эту книгу стоит купить. В виде алгоритма, что предлагает Кнут, это может выглядеть так:
#include <iostream>
#include <math.h>

using namespace std;

/* 4.5.4C */
void fermat(unsigned n)
{
	double x = 2*floor(sqrt((double)n)) + 1;
	unsigned y = 1;
	double r = pow((floor(sqrt((double)n))), 2) - n;

	for(;;)
	{
		if(!r)
		{
			cout << (x-y)/2 << endl;
			return;
		}

		r += x;
		x += 2;

		do
		{
			r -= y;
			y += 2;
		}while(r > 0);
	}
}


void main()
{
	fermat(377);
}


Дата: Авг 20, 2004 23:49:09

В этом разделе ранее был приведен популярный вероятностный тест Рабина-Миллера на простоту числа (был взят из Кнута). Не так давно тройка веселых индусов открыла куда более крутой детерминистический тест. Тест очень быстр. Хм. Но и не слишком-то прост :)
Реализацию теста можно, например, найти в Miracl.
Сама статья находится по адресу:

http://www.cse.iitk.ac.in/news/primality.html.

Чтобы написать этот алгоритм на С/С++ самому, вам потребуется большинство из процедур, приведенных в этом топике выше, а также процедура проверки числа на степень. Процедуру можно найти тут:

http://www.wasm.ru/forum/index.php?action=vthread&forum=17&topic=6888


Дата: Авг 21, 2004 14:13:25


Дата: Авг 21, 2004 14:55:23

Loger
„http://www.cse.iitk.ac.in/news/primality.html Error 404“
Как ни странно, после поиска в Гугле и закачки
старого варианта алгоритма
http://www.cse.iitk.ac.in/primality.pdf
страничка опять появилась. У них там все через базу данных организовано - видимо свой кеш почистили....


Дата: Ноя 1, 2004 11:43:59

В ссылке от volodya лишняя точка (после html), посему у Loger и Error 404 появлялось :))


Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.072