Пока s <<sqrt (N) </em>
Одна ошибка, которую люди часто делают в таких алгоритмах, это не предварительный расчет квадратного корня.
while (s <= sqrt(N)) {
намного, намного медленнее, чем
int limit = sqrt(N);
while (s <= limit) {
Но, вообще говоря, Eiko прав в своем комментарии. Если вы хотите, чтобы люди предлагали низкоуровневую оптимизацию, вы должны предоставить код.
обновление Хорошо, теперь о вашем коде.
Вы можете заметить, что количество итераций в вашем коде чуть больше, чем 'l'. (вы можете поместить счетчик внутри первого цикла for, он будет в 2-3 раза больше) И, очевидно, сложность вашего решения не может быть меньше, чем O (l) (вы не можете иметь меньше, чем 'l итераций).
Что может реально изменить, так это эффективный доступ к памяти. Обратите внимание, что парень, который написал эту статью, пытается уменьшить размер хранилища не только потому, что он жаден до памяти Создание компактных массивов позволяет лучше использовать кэш и, следовательно, увеличить скорость.
Я только что заменил логическое [] на int [] и добился немедленного увеличения скорости в 2 раза. (и 8x памяти) И я даже не пытался сделать это эффективно.
Update2
Это легко. Вы просто заменяете каждое присвоение a[i] = true
на a[i/32] |= 1 << (i%32)
и каждую операцию чтения a[i]
на (a[i/32] & (1 << (i%32))) != 0
. И boolean[] a
с int[] a
, очевидно.
Из первой замены должно быть понятно, как это работает: если f(i)
истинно, то в целом числе a[i/32]
есть бит 1
, в позиции i%32
(int
в Java точно 32 бита, как вы знаете).
Вы можете пойти дальше и заменить i/32
на i >> 5
, i%32
на i&31
. Вы также можете предварительно вычислить все 1 << j
для каждого j в диапазоне от 0 до 31 в массиве.
Но, к сожалению, я не думаю, что в Java вы могли бы приблизиться к C в этом. Не говоря уже о том, что этот парень использует много других хитрых оптимизаций, и я согласен, что он мог бы стоить намного больше, если бы он сделал комментарии.