powered by simpleCommunicator - 2.0.59     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / C++ [игнор отключен] [закрыт для гостей] / Генератор простых чисел (до 10^9 за 5 сек)
25 сообщений из 402, страница 1 из 17
Генератор простых чисел (до 10^9 за 5 сек)
    #38920516
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Тут 17438916 по быстрому накидал простой генератор простых чисел. Генерит первые 100 млн. за секунду, дальше начинается тормоз. Там же узнал про решето Эратосфена и Аткина. Решил потестить.

Чего бы где не писали, но у меня получилось что Эратосфен быстрее Аткина в 3 раза на первых 800 млн. (дальше Аткин не может, похоже разрядности unsigned int не хватает).

Победитель, использует 62 Мб для рачета до 10^9 (запостите в википедию кто может, вектор prime выкинуть, printf() разремить):
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
std::vector<unsigned int> prime;

void eratosfen(unsigned int limit)
{
	unsigned int* bitmap = (unsigned int*) calloc(limit / 64 + ((limit & 63) ? 1 : 0), sizeof(unsigned int));
	//printf("2 3 ");
	prime.push_back(2);
	prime.push_back(3);
	unsigned int max_prime = 3;
	bool need_fill = true;
	while(need_fill) {
		unsigned int step = max_prime << 1;
		for(unsigned int i = max_prime * max_prime; i < limit; i += step) { // Вычеркиваем кратные max_pr
			bitmap[i >> 6] |= (1 << ((i >> 1) & 31));
		}
		if(max_prime * max_prime >= limit) need_fill = false; // дальше заполнять не надо
		// вычитываем следущую порцию
		for(unsigned int i = max_prime + 2; i < limit; i += 2) {
			if((bitmap[i >> 6] & (1 << ((i >> 1) & 31))) == 0) {
				prime.push_back(i);
				//printf("%u ", i);
				if(need_fill) {
					max_prime = i;
					break;
				}
			}
		}
	}
	free(bitmap);
}



Решето Аткина
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
std::vector<unsigned int> prime;

void atkin(unsigned int limit)
{
	unsigned int* bitmap = (unsigned int*) calloc(limit / 32 + 1 + ((limit & 31) ? 1 : 0), sizeof(unsigned int));
	bitmap[0] = 12;
	unsigned int sqr_lim = (unsigned int)sqrt((long double)limit);
 
	// Предположительно простые - это целые с нечетным числом
	// представлений в данных квадратных формах.
	// x2 и y2 - это квадраты i и j (оптимизация).
	unsigned int x2 = 0;
	for (unsigned int i = 1; i <= sqr_lim; i++) {
		x2 += 2 * i - 1;
		unsigned int y2 = 0;
		for (unsigned int j = 1; j <= sqr_lim; j++) {
			y2 += 2 * j - 1;
	 
			unsigned int n = 4 * x2 + y2;
			if ((n <= limit) && (n % 12 == 1 || n % 12 == 5))
				bitmap[n >> 5] ^= (1 << (n & 31));
	 
			n -= x2; // Оптимизация n = 3 * x2 + y2; 
			if ((n <= limit) && (n % 12 == 7)) {
				bitmap[n >> 5] ^= (1 << (n & 31));
			}
	 
			n -= 2 * y2; // Оптимизация n = 3 * x2 - y2;
			if ((i > j) && (n <= limit) && (n % 12 == 11)) {
				bitmap[n >> 5] ^= (1 << (n & 31));
			}
		}
	}
	 
	// Отсеиваем кратные квадратам простых чисел в интервале [5, sqrt(limit)].
	// (основной этап не может их отсеять)
	for (unsigned int i = 5; i <= sqr_lim; i++) {
		if (bitmap[i >> 5] & (1 << (i & 31))) {
			unsigned int n = i * i;
			for (unsigned int j = n; j <= limit; j += n) {
				bitmap[j >> 5] &= ~(1 << (j & 31));
			}
		}
	}
	 
	// Вывод списка простых чисел в консоль.
	//printf("2, 3, 5"); 
	prime.push_back(2);
	prime.push_back(3);
	prime.push_back(5);
	for(unsigned int i = 7; i <= limit; i += 2) {  
		if ((bitmap[i >> 5] & (1 << (i & 31)))){
		  // printf(", %d", i);
		   prime.push_back(i);
		}
	}
	free(bitmap);
}



Обоих с википедии взял. Допилил сколько смог. Добавил биткарту. Правда в Эратосфене она в два раза меньше, т.к. четные пропустил. В Аткине так не получилось, т.к. второй цикл (по 5-кам попадает на четные). Но и с биткартой без четных он все равно медленнее (в 2,5 раза), поэтому не стал дальше разбираться. Если кто будет ускорять Аткина - постите сюда что получилось. Мое ИМХУ в Аткине слишком много получения остатка от деления, а память нынче достаточно быстрая, поэтому Эратосфен быстрее, т.к. там только битовые операции.
И потом Эратосфена проше досчитывать.
Асинхронный Эратосфен. Получение следующего простого после X
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
unsigned int next_prime(unsigned int x)
{
	static std::vector<unsigned int> cache;
	static unsigned int max_prime = 0;
	if(max_prime == 0) {
		cache.push_back(2);
		cache.push_back(3);
		max_prime = 3;
	}
	if(x >= max_prime) { // нет в кэше
		unsigned int limit = (x < 1000) ? 1024 : x /4 * 5; // x+20%

		unsigned int start = max_prime + 2; // должно быть четное
		unsigned int* bitmap = (unsigned int*) calloc((limit - start) / 64 + 1, sizeof(unsigned int));
		limit -= start;

		if(max_prime > 3) { // инициализация предыдущим расчетом
			unsigned int* end = &cache[cache.size() - 1];
			for(unsigned int* cur = &cache[1];cur <= end; cur++) {
				unsigned int i = *cur * *cur; 
				if(i > limit + start) break;
				unsigned int step = *cur << 1;
				if(i < start) { // выравнивание под cur*cur+2N*cur
					i = start - (start - i) % step;
					if(i < start) i += step;
				}
				i -= start;
				//printf("start=%d i=%d max=%d\n", start, i, max_prime);
				for(; i < limit; i += step) { // Вычеркиваем кратные max_prime
					bitmap[i >> 6] |= (1 << ((i >> 1) & 31));
				}
			}
		}

		bool need_fill = true;
		while(need_fill) {
			if(max_prime >= 65536 || max_prime * max_prime >= limit + start) {
				need_fill = false; // дальше заполнять не надо
			} else {
				unsigned int step = max_prime << 1;
				for(unsigned int i = max_prime * max_prime - start; i < limit; i += step) { // Вычеркиваем кратные max_prime
					bitmap[i >> 6] |= (1 << ((i >> 1) & 31));
				}
			}
			// вычитываем следущую порцию
			for(unsigned int i = max_prime + 2 - start; i < limit; i += 2) {
				if((bitmap[i >> 6] & (1 << ((i >> 1) & 31))) == 0) {
					cache.push_back(i + start);
					if(need_fill) {
						break;
					}
				}
			}
			max_prime = cache.back();
		}
		free(bitmap);
	}

	static unsigned int prev_id = 0;
	static unsigned int prev_prime = 0;
	if(x < 2) {
		if(x == 0) { // освобождение памяти
			max_prime = 0;
			cache.clear();
		}
		prev_id = 0;
		prev_prime = 2;
	} else if(x == prev_prime) {
		prev_id++;
		prev_prime = cache[prev_id];
	} else { // поиск в кэше
		unsigned int* min = &cache[0];
		unsigned int* max = &cache[cache.size() - 1];
		while(max - min > 1) {
			unsigned int* mid = min + (max - min) / 2;
			if (*mid > x) {
				max = mid;
			} else {
				min = mid;
			}
		}
		prev_id = max - &cache[0];
		prev_prime = *max;
	}
	return prev_prime;
}

// Пример использования
int k = 1;
while(k < 1000) {
   k = next_prime(k);
   printf("%d ", k);
}



Действительно ли числа простые : сравнивал результаты разных алгоритмов между собой, до 100 млн. с 17438916 , до 800 млн. с Аткиным, до 2 млрд. синхронного и асинхронного Эратосфенов, дальше они считают, но памяти в x32 не хватает чтобы результаты обоих хранить, а так асинхронный до 3 млрд. за 15 сек считает. ИМХУ можно под x64 переточить, но это уже лишнее, надо больше - есть мат.либы.
Модератор: По просьбе автора ссылки изменены
next_prime32.h
next_prime64.h
Тесты и примеры использования
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38920536
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сразу скажу что я еще нечитал эти сорцы. Но поверхностно... не вижу в них новой идеи.

Поэтомо воспроизведу свои старые мысли по сабж.

Поиск и исследование простых чисел это увлекательнейшая часть математики. Если ее копнуть
глубоко то ты зацепишь краем практически ВСЕ разделы математических наук.

По поводу "решета". Я считаю его неэффективным на больших числах. Потому-что плотность простых
чисел падает от величины. Чем больше ваше искомое prime-число
тем больше "холостых вычёркиваний" в биткарте вам нужно сделать. В конце-концов поиск
упирается в эффективность использования памяти. Для современной рабочей станции
(адресующей 16Гб) можно адресовать примерно.

16Гб = 17,179,869,184 байт = 137,438,953,472 бит = bitcartCap

Тоесть 137 млрд

Количество простых чисел на этом интервале (ЕМНИП) равно n/ln(n). Посчитаем приблизительно.

137,438,953,472 / ln (137,438,953,472) ~ 5 358 986 394

Тоесть 5 млрд простых чисел в биткарте. А какова эффективность? Примерно на 26 бит приходится 1
простое число. И с ростом числа эта самая эффективность хранения будет падать.

Надеюсь я не ошибся. Если что - прошу поправить.

Как считать дальше? - Неизвестно. Возможно по биткарте можно построить итератор
и извлекать из него биты но эффективность этого метода у меня под вопросом.
Так что после генерации биткарту следовало-бы уничтожить и данные сохранить
в обычный вектор. И положить в текстовый файл.

Можно мечтать и расчитывать что старик Мур еще подкинет нам сюрпризы типа
удвоения объёма памяти за ту-же цену. Но.... я-бы не особо на это надеялся.
Primes - коварны. И как-только мы выйдем из одной трудности - тут-же вступим
в другую. Хотя-бы рассмотреть неэффективность "арифметического деления
по модулю" для целых чисел длинной 64, 128, 256 бит.

Интересно также рассмотреть различные варианты сжатия биткарт. Использования деревьев
для хранения таких "разрежённых" объемов. И прочих нестандартных структур.

P.S. Жаль Базист сбежал. Он вобщем-то был неплохой оппонент и собеседник по структурам данных.
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38920543
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
maytonне вижу в них новой идеи.
Ее не было. Я же написал тестил старые. Все пишут что что Аткин быстрее Эратосфена. У меня обратное получилось.

maytonДля современной рабочей станции (адресующей 16Гб) можно адресовать примерно.

16Гб = 17,179,869,184 байт = 137,438,953,472 бит = bitcartCap

Тоесть 137 млрд

еще на 2 умножь, четные выкинул, 1 байт = 16 чисел.

maytonТак что после генерации биткарту следовало-бы уничтожить и данные сохранить
в обычный вектор. И положить в текстовый файл.
Мой асинхронный вариант так и делает. Кэширует результаты и досчитывает небольшой биткаротой сверх предыдущего расчета.

Цели считать далеко не ставил. Эратосфеном можно посчитать простое X зная все простые до sqrt(X), т.е. с 16 Гб памяти можно рассчитать простое порядка 5*10^22. В принципе алгоритм параллелится.

Изначально цель была простая: не вставлять в код статические таблицы, заменить быстрым генератором.
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38920550
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima TЕе не было. Я же написал тестил старые. Все пишут что что Аткин быстрее Эратосфена. У меня обратное получилось.

Да это странно. Интересно профилировать метод Аткина чтобы понять что можно улучшить.

еще на 2 умножь, четные выкинул, 1 байт = 16 чисел.
Это плюс. А если-б ты еще и "тройки" виртуально выкинул? А?

Мой асинхронный вариант так и делает. Кэширует результаты и досчитывает небольшой биткаротой сверх предыдущего расчета.

Цели считать далеко не ставил. Эратосфеном можно посчитать простое X зная все простые до sqrt(X), т.е. с 16 Гб памяти можно рассчитать простое порядка 5*10^22. В принципе алгоритм параллелится.

Изначально цель была простая: не вставлять в код статические таблицы, заменить быстрым генератором.
Кстати MOD(x,12) по Уоррену заменяется на одно умножение
на магическое число и на один shift магическое число раз.
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38920591
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Дмитрий, если не ошибаюсь очень слабо растёт. Потому сложности алгоритмов должны быть сравнимы с константой, и разница должна быть заметна на очень больших объёмах, хоты бы 10^50 элементов. Может быть я путаю конечно, но по-моему так.

Мы сейчас уехжаем, потому сегодня не смогу сам написать код и проверить ((
Завтра займусь :)
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38920611
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
maytonПримерно на 26 бит приходится 1 простое число.
Хорошая мысль, в векторе хранить невыгодно. Посчитал статистику. В диапазоне х32 в моей биткарте получается в среднем 11 бит на число. В векторе 32, т.е. биткарта в 3 раза меньше памяти расходует. Биткарта 256 Мб на все числа до 2^32. Чисел ~190 млн., т.е. массив будет 760 Мб. Я поэтому от int64 отказался.
Статистика расчета 4 млрд. по 100 млн.Расчет доКол-во простыхСреднее распределениеВремя расчета мс1000000005761456182602000000005317482192803000000005173388202414000000005084001203505000000005019541203316000000004968836211907000000004928228214418000000004893248213109000000004863036213101000000000483831921381110000000048149362130120000000047922352153113000000004773628215511400000000475714022301500000000474105522661160000000047253052278117000000004711186223018000000004699403221001190000000046855062230200000000046743592220210000000046642392210722200000000465359622202300000000464481822124224000000004633507223025000000004624924222026000000004618796221642270000000046080252230280000000046000662220290000000045937532216633000000000458552622303100000000457910422203200000000457216422303300000000456502422191234000000004559367223035000000004554137222036000000004547803223137000000004542381232183380000000045366772330390000000045304312330400000000045251872320

Время неравномерно, т.к. дорасчет идет порциями +12,5% (max_prime/8*9, в изначальном варианте было +20%, поправил чтоб за разрядность х32 не вылезти)
maytonКоличество простых чисел на этом интервале (ЕМНИП) равно n/ln(n)
Если так, то в биткарте всегда плотность хранения будет выше чем в массиве, т.к. ln(2^N) < N

maytonДа это странно. Интересно профилировать метод Аткина чтобы понять что можно улучшить.
...
Кстати MOD(x,12) по Уоррену заменяется на одно умножение
на магическое число и на один shift магическое число раз.
Надо поискать чудоформулу, это должно заметно ускорить Аткина.
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38920619
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SashaMercuryДмитрий, если не ошибаюсь очень слабо растёт. Потому сложности алгоритмов должны быть сравнимы с константой, и разница должна быть заметна на очень больших объёмах, хоты бы 10^50 элементов. Может быть я путаю конечно, но по-моему так.
Судя по моим замерам похоже что так: 1 млрд ~5 сек, 3 млрд ~15 сек. Тут проблема в другом, для больших чисел памяти надо много под биткарты. Хотя тоже решаемо: для поиска следующего простого после любого X можно сгенерить кусок биткарты в 5 Мб (70 млн. чисел если тут не ошибаются ) и последовательно инициализировать ее всеми простыми до sqrt(X + 70 млн). В общем чем больше диапазон покрывает биткарта, тем больше чисел находится за раз.
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38920625
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima TmaytonКоличество простых чисел на этом интервале (ЕМНИП) равно n/ln(n)
Если так, то в биткарте всегда плотность хранения будет выше чем в массиве, т.к. ln(2^N) < N

maytonДа это странно. Интересно профилировать метод Аткина чтобы понять что можно улучшить.
...
Кстати MOD(x,12) по Уоррену заменяется на одно умножение
на магическое число и на один shift магическое число раз.
Надо поискать чудоформулу, это должно заметно ускорить Аткина.
Я не очень понял твою формулу ln(2^n) < N. Откуда она?

По поводу Уоррена я немного ошибся. Старик писал про оптимизацию целочисленного деления.

Вот из моих сорцов. К сожалению делители только до 10.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
#define OPTDIV_2(num)  num >> 1
#define OPTDIV_3(num)  num * 683 >> 11
#define OPTDIV_4(num)  num >> 2
#define OPTDIV_5(num)  num * 205 >> 10
#define OPTDIV_6(num)  num * 683 >> 12
#define OPTDIV_7(num)  num * 1171 >> 13
#define OPTDIV_8(num)  num >> 3
#define OPTDIV_9(num)  num * 911 >> 13
#define OPTDIV_10(num) num * 205 >> 11



Но для расчёта остатков нетрудно дописать еще разность и умножение.

Код: plaintext
1.
#define OPTMOD_10(num) 10 * num - OPTDIV_10(num)



По поводу Аткина. Я обращаю внимание наwikiНиже представлена реализация упрощённой версии на языке программирования C++, иллюстрирующая основную идею алгоритма — использование квадратичных форм:
Что означает упрощённая ? Грубая? Без оптимизаций? Краткая?

Ну и ... стоить покурить ассемблер. Недавно с Сашиком обсуждали тему
получение и частного и остатка одним махом.
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38920673
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
maytonЯ не очень понял твою формулу ln(2^n) < N. Откуда она?
Из твоей вывел:
maytonКоличество простых чисел на этом интервале (ЕМНИП) равно n/ln(n)
Для обычного хранения числа размера 2^N надо N бит
хранение массивом N * 2^N / ln(2^N) бит
биткартой 2^N бит
делим, получаем 1 биткарта = N / ln(2^N) = 1/ln(2) = 1,4427 массива
т.е. плотность хранения в биткарте всегда будет выше в 1,4427.
Т.к. я четные выкинул, то моя биткарта всегда будет занимать меньше места в 2,885 раза по сравнению с массивом.

maytonПо поводу Уоррена я немного ошибся. Старик писал про оптимизацию целочисленного деления.

Вот из моих сорцов. К сожалению делители только до 10.
Направление понял. Можно попробовать подбором поискать. Может есть.
Остаток от деления на 5 тоже пригодится.
Тут только минус - запас разрядности надо. Если не путаю, на x32 сдвиги 32 битами ограничены. Надо на x64 тестить.

maytonПо поводу Аткина. Я обращаю внимание наwikiНиже представлена реализация упрощённой версии на языке программирования C++, иллюстрирующая основную идею алгоритма — использование квадратичных форм:
Что означает упрощённая ? Грубая? Без оптимизаций? Краткая?
Просто корявая какая-то, расчет квадратов оптимизирован, а при считывании результатов вместо того чтобы сделать циклу шаг 2 добавили проверку кратности 3 и 5. С забавным примечанием. Я немного причесал, но не уверен что алгоритм полностью соблюден.
Надо еще поискать реализации и в алгоритм посильнее вникнуть, я не понял зачем там инверсии битов.

maytonНу и ... стоить покурить ассемблер. Недавно с Сашиком обсуждали тему
получение и частного и остатка одним махом.
Я там c вами курил, есть команда div в асме, в си div(), только компилятор MS ее в функцию обернул и в тормоз превратил. Надо глянуть во что % компилируется.
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38920726
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima TНадо глянуть во что % компилируется.
Глянул, ничего лишнего:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
	int s = 100;
00413E9D  mov         dword ptr [s],64h 
	int z = s%12;
00413EA4  mov         eax,dword ptr [s] 
00413EA7  cdq              
00413EA8  mov         ecx,0Ch 
00413EAD  idiv        eax,ecx 
00413EAF  mov         dword ptr [z],edx 


Значит тормоза внутри проца
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38920736
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima TДля обычного хранения числа размера 2^N надо N бит
хранение массивом N * 2^N / ln(2^N) бит
биткартой 2^N бит
делим, получаем 1 биткарта = N / ln(2^N) = 1/ln(2) = 1,4427 массива
т.е. плотность хранения в биткарте всегда будет выше в 1,4427.
Т.к. я четные выкинул, то моя биткарта всегда будет занимать меньше места в 2,885 раза по сравнению с массивом.

Я понял почему я тебя не понял. Я разрядность int не считал. Мои увлечения primes охладели с резкой нехваткой оперативки.
И я вобщем-то не планировал использовать массивы. Меня интересовали дисковые способы хранения расчитанных primes.
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38920740
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima TЗначит тормоза внутри проца
Это краеугольный камень криптографии. Именно принципиальная невозможность
оптимизировать DIV/MOD (в частности факторизацию) обеспечивает безопасность нам всем сегодня.
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38920751
RWolf
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton,

криптография оперирует простыми числами несравнимо больше 10^9, эти алгоритмы к ней отношения не имеют, так что оптимизация div для неё бесполезна.
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38920785
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
maytonИ я вобщем-то не планировал использовать массивы. Меня интересовали дисковые способы хранения расчитанных primes.
Выше писал как бесконечно можно генерить с минимум 5 мб оперативки:
генеришь до 5 млн, пишешь в файл, делаешь решето под 5-10, считываешь посчитанные последовательно с файла до sqrt(10 млн), каждым числом проходишь по решету, получаешь все простые в интервале 5-10, результат в конец файла, и т.д. пока место на диске не кончится

Написал, подумал, затестил: кусками по 5 мб в два раза быстрее считает чем в один проход.
До 10^9 было 4,5 сек
Код: plaintext
1.
unsigned int limit = (x < 1000) ? 1024 : x /4 * 5; // x+20%


Стало 2,65 после замены на
Код: plaintext
1.
unsigned int limit = x + 5000000; // x+5Mb


Заменил на +1 Мб стало 2.53 сек.

Наверно потому что в L3 кэш проца целиком ложится. А когда одним большим куском считешь гоняется проц-память при каждом проходе.
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38921112
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Можно назвать этот метод "Хождение по решёткам". (Решетам?)

P.S. Как будете решето в множ. числе?
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38921635
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Асинхронный Эратосфен Версия 2.0
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
unsigned int next_prime(unsigned int x)
{
	static unsigned int* bitmap = NULL; // биткарта
	static unsigned int bm_size = 0; // размер биткарты в байтах
	static unsigned int limit = 0; // до скольки заполнять решето

	unsigned int ret = 0;
	while(true) {
		if(x < 3) {
			if(x == 0) { // освобождение памяти
				limit = 0;
				bm_size = 0;
				if(bitmap) {
					free(bitmap);
					bitmap = NULL;
				}
			}
			ret = (x < 2) ? 2 : 3;
			break;
		}
		// поиск следующего после x
		for(unsigned int i = (x | 1) + 2; i < limit; i += 2) {
			if((bitmap[i >> 6] & (1 << ((i >> 1) & 31))) == 0) {
				ret = i;
				break;
			}
		}
		if(ret != 0) break;
		// нехватило биткарты, дорасчет
		unsigned int old_limit = limit;
		limit += 1048576; // 64Kb
		if(limit/16 >= bm_size) { // довыделение памяти
			unsigned int bm_size_new = bm_size / 4 * 5;
			if(limit/16 > bm_size_new) bm_size_new = limit/16;
			if((x + 1048576)/16 > bm_size_new) bm_size_new = ((x + 1048576)/16) + 4; 
			if(bm_size_new > 0x10000000) bm_size_new = 0x10000000; // 2^(sizeof(unsigned int)/16)
			unsigned int* p = (unsigned int*) realloc(bitmap, bm_size_new); 
			if(!p) {
				printf("No memory. New size %d byte\n", bm_size_new);
				ret = 0xFFFFFFFF;
				break;
			}
			bitmap = p;
			memset(bitmap + bm_size/4, 0, bm_size_new - bm_size);
			bm_size = bm_size_new;
		}
		unsigned int max_prime = 3; // максимальное простое
		while(true) {
			unsigned int i = max_prime * max_prime; 
			if(i >= limit) break; // дальше заполнять не надо
			unsigned int step = max_prime << 1;
			if(i < old_limit) { // выравнивание под max_prime*max_prime+2N*max_prime
				i = old_limit - (old_limit - i) % step;
				if(i < old_limit) i += step;
			}
			for(; i < limit; i += step) { // Вычеркиваем кратные max_prime
				bitmap[i >> 6] |= (1 << ((i >> 1) & 31));
			}
			// ищем следущее простое
			for(unsigned int i = max_prime + 2; i < limit; i += 2) {
				if((bitmap[i >> 6] & (1 << ((i >> 1) & 31))) == 0) {
					max_prime = i;
					break;
				}
			}
		}
	}
	return ret;
}


Учел все вышенайденное. Скорость возросла в 2 раза по сравнению с синхронным. До 4 млрд. за 8.7 сек.
Что поменялось:
1. За раз считается 1 млн. значений (64 Кб биткарты).
2. Экономнее расходуется память. Массив найденных не хранится. Только биткарта, она компактнее почти в 3 раза.

Особенность: почти половина времени уходит на чтение результатов:
Код: plaintext
1.
next_prime(4000000000); //считается 4,7 сек

после
Код: plaintext
1.
2.
unsigned int k = 1;
while(k < 4000000000) k = next_prime(k); // 3,7 сек.


Т.е. если надо много раз пройтись по расчитанному - лучше закэшировать в массив. В предыдущем варинте сразу писалось в массив, поэтому нельзя было съэкономить на ненужных сохранениях.
Если сразу второе запустить то будет 8,7 сек., т.е. на 0,3 сек медленнее. Плата за экономию памяти. В первом случае выделяется сразу столько, сколько нужно.
Проверка что до 4 млрд посчитано правильно прошла успешно
Код: plaintext
1.
2.
3.
4.
5.
6.
		unsigned int max = next_prime(4000000000);
		unsigned int xe = 2;
		while(xe < 4000000000) {
			if(max % xe == 0) {printf("\nerror max = %u xe=%u\n", max, xe); break;}
			xe = next_prime(xe);
		}


То же самое x64Не совсем x64, должно работать до 64 млрд., дальше realloc() не выделит более 4 Гб под биткарту. Сравнивал до 4 млрд. с версией х32 дальше чего нагенерит не проверял.
При компиляции под x64 работает так же быстро, под x32 в 1,5 раза медленнее.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
typedef unsigned long long uint64_t;

uint64_t next_prime_64(uint64_t x)
{
	static unsigned int* bitmap = NULL; // биткарта
	static unsigned int bm_size = 0; // размер биткарты в байтах
	static uint64_t limit = 0; // до скольки заполнять решето

	uint64_t ret = 0;
	while(true) {
		if(x < 3) {
			if(x == 0) { // освобождение памяти
				limit = 0;
				bm_size = 0;
				if(bitmap) {
					free(bitmap);
					bitmap = NULL;
				}
			}
			ret = (x < 2) ? 2 : 3;
			break;
		}
		// поиск следующего после x
		for(uint64_t i = (x | 1) + 2; i < limit; i += 2) {
			if((bitmap[i >> 6] & (1 << ((i >> 1) & 31))) == 0) {
				ret = i;
				break;
			}
		}
		if(ret != 0) break;
		// нехватило биткарты, дорасчет
		
		uint64_t old_limit = limit;
		limit += 1048576; // 64Kb
		if(limit/16 >= bm_size) { // довыделение памяти
			unsigned int bm_size_new = bm_size / 4 * 5;
			if((unsigned int)limit/16 > bm_size_new) bm_size_new = (unsigned int)limit/16;
			if((unsigned int)(x + 1048576)/16 > bm_size_new) bm_size_new = (unsigned int)((x + 1048576)/16) + 4; 
			if(bm_size_new < bm_size) bm_size_new = 0xFFFFFFFF; // превышение разрядности
			unsigned int* p = (unsigned int*) realloc(bitmap, bm_size_new); //calloc(limit / 64 + ((limit & 63) ? 1 : 0), sizeof(unsigned int));
			if(!p) {
				printf("No memory. New size %u byte\n", bm_size_new);
				ret = 0xFFFFFFFF;
				break;
			}
			//if(bitmap != p)	printf("Move %d bytes\n", old_limit/16);
			bitmap = p;
			memset(bitmap + bm_size/4, 0, bm_size_new - bm_size);
			bm_size = bm_size_new;
		}
		uint64_t max_prime = 3; // максимальное простое
		while(true) {
			uint64_t i = max_prime * max_prime; 
			if(i >= limit) break; // дальше заполнять не надо
			uint64_t step = max_prime << 1;
			if(i < old_limit) { // выравнивание под max_prime*max_prime+2N*max_prime
				i = old_limit - (old_limit - i) % step;
				if(i < old_limit) i += step;
			}
			for(; i < limit; i += step) { // Вычеркиваем кратные max_prime
				bitmap[i >> 6] |= (1 << ((i >> 1) & 31));
			}
			// ищем следущее простое
			for(uint64_t i = max_prime + 2; i < limit; i += 2) {
				if((bitmap[i >> 6] & (1 << ((i >> 1) & 31))) == 0) {
					max_prime = i;
					break;
				}
			}
		}
	}
	return ret;
}



По поводу Аткина:
1. Эмулятор %12 не взлетит на x32 даже если его найти. Т.к. битовые сдвиги 64-битных переменных в x32 реализованы программно.
2. Аткин медленнее в 5,6 раза этого варианта.
Вывод: заход далеко за область расчета не дает посчитать более чем до 800 млн. на x32. Возможно на x64 взлетит если переделать на расчет кусками, но я эти подвиги не буду совершать, оставим поле для творчества следующим поколениям :)
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38921677
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Круть. Поверхностный code-review даже ни к чему ни цепляется. Думаю что улучшать можно
только в направлении теории алгоритмов или спуска на уровни Ассемблеров.

Еще есть одно решето. Еще один алгоритм одного индуса.
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38921786
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ребята, что со скоростью ? Можно показать таблицу для 10^7,10^8, 10^9 для двух методов, со значениями скорости и памяти, пожалуйста
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38921787
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Где бы запустить эти алгоритмы для 10^20 элементов
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38921813
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Кстати, почему отсев Аткина завязан на числе 60 ?
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38921847
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Дмитрий, так у тебя упрощенный вариант, я сначала не понял. Деление происходит на 12. В оригинальной работе , используется число 60. И как я понял, это число должно динамически менять в зависимость от мощности исследуемого множества
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38921850
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Здесь .... эээ думаю помогут мои исторические знания. 60 - это основание древней вавилонской
системы счисления. 60 уникально тем что имеет следующие делители 1,2,3,4,5,6. С шагом 1.
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38921862
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SashaMercuryРебята, что со скоростью ? Можно показать таблицу для 10^7,10^8, 10^9 для двух методов, со значениями скорости и памяти, пожалуйста
Я-бы предложил для начала прогнать модульный тест на корректность.
Иначе мы в погоне за скоростью забудем самое главное.
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38921893
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Затестил x32 версию - ищет все до 2^32.
Ошибок нет, только зацикливается после последнего.
чтобы возвращала 0 по окончании, после строчки
Код: plaintext
1.
		limit += 1048576; // 64Kb


добавить
Код: plaintext
1.
2.
3.
4.
		if(limit < old_limit) { // превышение разрядности
			printf("No more primes :(\n");
			break;
		}


SashaMercuryРебята, что со скоростью ? Можно показать таблицу для 10^7,10^8, 10^9 для двух методов, со значениями скорости и памяти, пожалуйста
Памяти надо 1 байт на каждые 16 чисел. Т.е. для расчета 10^9 надо ~62 Мб. Реально довыделяется +20% к имеющемуся, чтобы не сильно тормозило из-за realloc()
Скорость померь сам. Пример замера скорости
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
	next_prime(0); // очистка памяти
	unsigned int x = next_prime(1); // выделение памяти
	clock_t t = clock();
	x = next_prime(10000000);
	printf("10^7=%u time %d msec\n", x, clock() - t);

	next_prime(0);
	x = next_prime(1);
	t = clock();
	x = next_prime(100000000);
	printf("10^8=%u time %d msec\n", x, clock() - t);

	next_prime(0);
	x = next_prime(1);
	t = clock();
	x = next_prime(1000000000);
	printf("10^9=%u time %d msec\n", x, clock() - t);

	next_prime(0);
	x = next_prime(1);
	t = clock();
	while(x < 1000000000) x = next_prime(x);
	printf("all to 10^9=%u time %d msec\n", x, clock() - t);


У меня такой результат10^7=10000019 time 20 msec
10^8=100000007 time 110 msec
10^9=1000000007 time 1101 msec
all to 10^9=1000000007 time 2143 msec

Для чистоты эксперимента последнего замера, чтобы исключить перевыделение памяти, задай в next_prime() сразу выделение максимума
Код: plaintext
1.
bm_size_new = 0x10000000; // 2^(sizeof(unsigned int)/16)


вместо if(bm_size_new > 0xB000000) ...
SashaMercuryГде бы запустить эти алгоритмы для 10^20 элементов
Если считать все до 10^20, то надо под хранение результата ~10^19 байт или 10^7 терабайт, думаю мало у кого столько есть. Задача отпадает.
Можно попробовать посчитать какой-нибудь диапазон, например 10^20...10^20 + 70 млн., для этого надо посчитать все до 10^10 (625 Мб памяти под хранение). Инициализировать ими решето только для этого диапазона и снять результат.
Только тут засада, 64 битная переменная до 1,8*10^19. Можешь порешать задачу: поиск максимально большого простого x64. Думаю за минуту должно посчитаться.
Дальше все резко усложняется из-за ограничения разрядности целых переменных.
maytonЕще есть одно решето. Еще один алгоритм одного индуса.
Решето Сундарама Оно (как и Аткин) заточено на поиск всех от 1 до N.
mayton, просьба, добавь в первый пост ссылку на окончательный вариант
В конец:
UPD Асинхронный Эратосфен. Версия 2.0 17451792
...
Рейтинг: 0 / 0
Генератор простых чисел (до 10^9 за 5 сек)
    #38921910
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
maytonЯ-бы предложил для начала прогнать модульный тест на корректность.
Иначе мы в погоне за скоростью забудем самое главное.
Это было с самого начала. Делал проверки:
Dima T Действительно ли числа простые : сравнивал результаты разных алгоритмов между собой, до 100 млн. с 17438916 , до 800 млн. с Аткиным, до 2 млрд. синхронного и асинхронного Эратосфенов.
Проверка от 4 млрд. до 2^32
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
		unsigned int x = next_prime(4000000000);
		while(x) {
			unsigned int y = 2;
			while(y < 65536) {
				if(x % y == 0) printf("error %u %u\n", x, y);
				y = next_prime(y);
			}
			x = next_prime(x);
		}


Так все можно, только ждать устанешь, эти 250 млн. проверялись с полчаса

По хорошему еще бы на пропуски проверить после 800 млн. До 800 совпало с Аткиным, а после алгоритмы одинаковые.
...
Рейтинг: 0 / 0
25 сообщений из 402, страница 1 из 17
Форумы / C++ [игнор отключен] [закрыт для гостей] / Генератор простых чисел (до 10^9 за 5 сек)
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]