Сита Эратосфена реализации и сравнения - PullRequest
0 голосов
/ 14 февраля 2020

Помимо простой реализации Сита Эратосфена с временной сложностью O (N log log N), я также пытался реализовать модификацию с временной сложностью O (N). Хотя оба результата дают желаемый результат, как-то раньше, чем раньше, требуется гораздо меньше времени по сравнению со следующим, и я не могу понять, почему. Я был бы очень признателен за некоторые советы по этому вопросу.

Реализация 1:

def build_sieve_eratosthenes(num):
    ## Creates sieve of size (num+1) to correct for 0-indexing.
    primes_sieve = [1] * (num+1)
    primes_sieve[0] = primes_sieve[1] = 0
    for p in range(2, num):
        if primes_sieve[p] == 1:
            for mul in range(2*p, num+1, p):
                primes_sieve[mul] = 0
    return primes_sieve

Реализация 2:

def build_sieve_eratosthenes_linear(num):
    ## Creates sieve of size (num+1) to correct for 0-indexing.
    primes_sieve = [1] * (num+1)
    primes_sieve[0] = primes_sieve[1] = 0

    ## Builds a list of size (num+1) recording the smallest prime factor of each number.
    SPF = [1] * (num+1)

    ## Builds a list of all primes seen so far with pos indicator of position where to insert the next prime.
    ## Uses a large fixed memory allocation scheme to avoid the usage of append list operation.
    primes = [0] * num
    pos = 0

    for p in range(2, num+1):
        if primes_sieve[p] == 1:
            primes[pos] = p
            pos = pos + 1
            ## Smallest prime factor of a prime is a prime itself.
            SPF[p] = p
        for i in range(0, pos):
            if p * primes[i] <= num and primes[i] <= SPF[p]:
                primes_sieve[p*primes[i]] = 0
                SPF[p * primes[i]] = primes[i]
            else:
                break
    return primes_sieve

test_num = 2000000

Метод испытания

def find_sum_of_primes_upto_num_with_sieve(sieve, num):
    primes_sieve = sieve(num)
    primes_sum = 0
    for n in range(len(primes_sieve)):
        if primes_sieve[n] == 1:
            primes_sum = primes_sum + n
    return primes_sum

Результаты:

start2 = time.time()
sum_2 = find_sum_of_primes_upto_num_with_sieve(build_sieve_eratosthenes, test_num)
end2 = time.time()
print("Sum of primes obtained: ", sum_2)
print("Time taken by checking primality of each number is %f sec" % (end2 - start2))

Сумма полученных простых чисел: 142913828922
Время, затраченное на проверку простоты каждого числа, составляет 0,647822 se c

start3 = time.time()
sum_3 = find_sum_of_primes_upto_num_with_sieve(build_sieve_eratosthenes_linear, test_num)
end3 = time.time()
print("Sum of primes obtained: ", sum_3)
print("Time taken by checking primality of each number is %f sec" % (end3 - start3))

Сумма полученных простых чисел: 142913828922
Время, затрачиваемое на проверку простоты каждого числа, составляет 1.561308 se c

1 Ответ

2 голосов
/ 15 февраля 2020

Я бросил простой счетчик итераций в каждую подпрограмму и побежал для степеней 10 от 10 ^ 3 до 10 ^ 7

build_sieve_eratosthenes:

    1000 has     1958 iterations in sieve
   10000 has    23071 iterations in sieve
  100000 has   256808 iterations in sieve
 1000000 has  2775210 iterations in sieve
10000000 has 29465738 iterations in sieve

build_sieve_eratosthenes_linear:

    1000 has      831 iterations in sieve_linear
   10000 has     8770 iterations in sieve_linear
  100000 has    90407 iterations in sieve_linear
 1000000 has   921501 iterations in sieve_linear
10000000 has  9335420 iterations in sieve_linear

Ваша linear функция не линейная: обратите внимание, что внутренняя l oop, которая выполняется pos раз ... и pos - это количество найденных простых чисел , не константа.

linear растет медленнее, чем "нормальная" функция, и имеет в целом значительно меньше итераций. Тем не менее, он имеет большую стоимость для каждой итерации, поэтому вы видите «перевернутые» времена. Каждое найденное число и каждое «вычеркивание» дороже в вашей функции linear; более медленный рост не достиг вашего предела в 2 * 10 ^ 6, а не моего предела 10 * 7. Вы можете экстраполировать это примерно на один день, чтобы лучше понять подходящее время, если оно того стоит ... но центральная "проблема" - более медленная обработка для каждого числа.

Для Любопытно подробно, вот полный вывод:

1000 has 1958 iterations in sieve
Sum of primes obtained:  76127
Time taken by checking primality of each number is 0.000904 sec
10000 has 23071 iterations in sieve
Sum of primes obtained:  5736396
Time taken by checking primality of each number is 0.008270 sec
100000 has 256808 iterations in sieve
Sum of primes obtained:  454396537
Time taken by checking primality of each number is 0.067962 sec
1000000 has 2775210 iterations in sieve
Sum of primes obtained:  37550402023
Time taken by checking primality of each number is 0.428727 sec
10000000 has 29465738 iterations in sieve
Sum of primes obtained:  3203324994356
Time taken by checking primality of each number is 5.761439 sec
1000 has 831 iterations in sieve_linear
Sum of primes obtained:  76127
Time taken by checking primality of each number is 0.001069 sec
10000 has 8770 iterations in sieve_linear
Sum of primes obtained:  5736396
Time taken by checking primality of each number is 0.010398 sec
100000 has 90407 iterations in sieve_linear
Sum of primes obtained:  454396537
Time taken by checking primality of each number is 0.107276 sec
1000000 has 921501 iterations in sieve_linear
Sum of primes obtained:  37550402023
Time taken by checking primality of each number is 1.087080 sec
10000000 has 9335420 iterations in sieve_linear
Sum of primes obtained:  3203324994356
Time taken by checking primality of each number is 11.008726 sec
...