Асимптотическая временная сложность алгоритма, как мне кажется, O ( n log n ) .
Внешний цикл работает для 2 ... sqrt(n)
.Внутренний цикл выполняется n / base
раз, где base
находится во внешнем диапазоне 2 ... sqrt(n)
.
Выполнение циклов приводит к общему количеству итераций, которое может быть выражено как:
(1) (n / 2) + (n / 3) + (n / 4) + ... + (n / sqrt(n))
Вышеуказанные круглые скобки используются для обозначения числа итераций внутреннего цикла в пределах одной итерации внешнего цикла.
Мы можем извлечь n
и получить
(2) n * (1/2 + 1/3 + 1/4 + ... + 1 / sqrt(n))
Заключенный в скобки термин - это ряд гармоник, который, как известно, расходится, поэтому мы не получаем ничего хорошего, как O (1) там, хотя расхождение чрезвычайно медленно.Это также подтверждается эмпирически вашей диаграммой, которая выглядит линейной.
Было показано, что ряд гармоник имеет постоянную связь с ln(n)
( source ).
Следовательно,мы получаем n * ln(n)
и, следовательно, сложность O ( n log n ) .
Вы неполучение более приятной O ( n log log n ) сложности, поскольку ваше решение не использует простую факторизацию (и, следовательно, ряды простых гармоник O (журнал n ) ( source )).
Практически, это потому, что ваш алгоритмпроверяет не простые числа, например, тот же индекс в arr[counter * base] = false;
назначен для пар base
и counter
{2, 6}, {3, 4}, {4, 3}, {6, 2}, но все же *Уже известно, что 1075 * 4 и 6 не являются простыми числами в той точке, в которой они применяются, и по определению алгоритма все их кратные также уже известны как не простые числа, и поэтому бесполезно проверять их снова.
РЕДАКТИРОВАНИЕ
An O ( n log log n ) Реализация JavaScript может выглядеть следующим образом:
function sieve(n)
{
// primes[x] contains a bool whether x is a prime
const primes = new Array(n + 1).fill(true);
// 0 and 1 are not primes by definition
primes[0] = false;
primes[1] = false;
function square(i)
{
return i * i;
}
for (let number = 2; square(number) <= n; number += 1)
{
if (!primes[number])
{
// we have already determined that the number is not a prime
// therefore all its multiples are also already determined not to be primes
// skip it
continue;
}
for (let multiplier = 2; multiplier * number <= n; multiplier += 1)
{
// a multiple of prime is not a prime
primes[multiplier * number] = false;
}
}
return primes;
}
Такой алгоритм все еще запускает внешний цикл sqrt(n)
раз, но теперь внутренний цикл запускается не для каждого числа, а только для простых чисел, поэтому для (2) мы теперь получаем
(2a) n * (1/2 + 1/3 + 1/5 + 1/7 + ... + 1 / (last_prime_less_or_equal_sqrt(n))
Как упоминалось ранее, заключенный в скобки термин представляет собой последовательность простых гармоник с log log n ростом.Это приводит нас к O ( n log log n ) , умноженному на n .