Сума найменших простих факторів


19

SF (n) - функція, яка обчислює найменший простий коефіцієнт для заданого числа n.

Будемо називати T (N) сумою кожного SF (n) з 2 <= n <= N.

T (1) = 0 (сума перевищує 0 сум)

T (2) = 2 (2 - перший простір)

T (3) = 5 = 2 + 3

T (4) = 7 = 2 + 3 + 2

T (5) = 12 = 2 + 3 + 2 + 5

...

T (10000) = 5786451

Переможцем стане той, кому вдасться за 60 секунд обчислити найбільший T (N) на своєму власному ноутбуці (Toshiba Satellite L845, Intel Core i5, 8 ГБ ОЗУ).


Current top score: Nicolás Siplis - 3.6e13 points - Nim

Pf (2) = 2, Pf (3) = 3, Отже, T (3) = 2 + 3 = 5. Я прав? Я дійсно запрограмований, щоб знайти основні фактори, але ви могли б детальніше розповісти про поточну вимогу. Дякую
Кодер

1
@ToddLehman Я запускаю кожен код у своєму власному ноутбуці (Sony Vaio SVF14A16CLB), тож якщо це займе менше 60 секунд, я збільшить кількість та зменшу його, коли це займе більше часу.
Nicolás Siplis

1
Так, доки він працює на моїй власній машині і видає правильну відповідь за 60 секунд або менше, це прийнятно.
Nicolás Siplis

1
Він має 4 нитки.
Nicolás Siplis

1
Чи дозволені сторонні бібліотеки? Це нормально, якщо програма створює теми?
Кодер

Відповіді:


12

Нім, 3,6е13

Просто просіювання не є найкращою відповіддю при спробі обчислити максимально можливий N, оскільки потреби в пам'яті стають занадто високими. Ось інший підхід (розпочався з Німом пару днів тому і закохався у швидкість та синтаксис; будь-які пропозиції щодо швидшого чи легшого читання вітаються!).

import math
import sequtils
import nimlongint # https://bitbucket.org/behrends/nimlongint/

proc s(n : int) : int128 =
    var x = toInt128(n)
    (x * x + x) div 2 - 1

proc sum_pfactor(N : int) : int128 =    
    var
        root = int(sqrt(float(N)))
        u = newSeqWith(root+1,false)
        cntA,cntB,sumA,sumB = newSeq[int128](root+1)
        pcnt,psum,ret : int128
        interval,finish,d,q,t : int

    for i in 0..root:
        cntA[i] = i-1
        sumA[i] = s(i)

    for i in 1..root:
        cntB[i] = N div i - 1
        sumB[i] = s(N div i)

    for p in 2..root:
        if cntA[p] == cntA[p-1]:
            continue

        pcnt = cntA[p - 1]
        psum = sumA[p - 1]
        q = p * p
        ret = ret + p * (cntB[p] - pcnt)
        cntB[1] = cntB[1] - cntB[p] + pcnt
        sumB[1] = sumB[1] - (sumB[p] - psum) * p
        interval = (p and 1) + 1
        finish = min(root,N div q)

        for i in countup(p+interval,finish,interval):

            if u[i]:
                continue

            d = i * p

            if d <= root:
                cntB[i] = cntB[i] - cntB[d] + pcnt
                sumB[i] = sumB[i] - (sumB[d] - psum) * p
            else:
                t = N div d
                cntB[i] = cntB[i] - cntA[t] + pcnt
                sumB[i] = sumB[i] - (sumA[t] - psum) * p

        if q <= root:
            for i in countup(q,finish-1,p*interval):
                u[i] = true

        for i in countdown(root,q-1):
            t = i div p
            cntA[i] = cntA[i] - cntA[t] + pcnt
            sumA[i] = sumA[i] - (sumA[t] - psum) * p

    sumB[1] + ret

var time = cpuTime()
echo(sum_pfactor(int(3.6e13))," - ",cpuTime() - time)

Я спробував реалізувати обгортку GMP для Nim у свій код, але просто не міг змусити його працювати (ніколи раніше не використовував GMP, що, звичайно, не допомогло).
Nicolás Siplis

Крім того, не потрібно returnв fвизначенні «S. Програми з одновираженням автоматично повертаються.
kirbyfan64sos

3
Це не перший найшвидший код, який Нім переміг помітною перевагою. Можливо, варто дослідити.
примо

Мені цікаво побачити, як він працює при використанні GMP, але не зміг його правильно реалізувати, незважаючи на мої зусилля.
Nicolás Siplis

Nim, безумовно, буде в моєму списку навчальних програм!
Sp3000

5

C, сито Prime: 5e9

Результати:

$ time ./sieve 
Finding sum of lowest divisors of n = 2..5000000000
572843021990627911

real    0m57.144s
user    0m56.732s
sys 0m0.456s 

Програма:

Незважаючи на те, що це досить чітка програма, у мене знадобився певний час, щоб зрозуміти, як правильно керувати пам'яттю - у мене достатньо оперативної пам’яті на 1 байт на число в діапазоні, тому мені довелося бути обережним. Це стандартне сито Erasthones.

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<assert.h>

#define LIMIT ((unsigned long long)5e9 +1)
#define ROOT_LIMIT floor(sqrt(LIMIT))

int main()
{
    printf("Finding sum of lowest divisors of n = 2..%llu\n", LIMIT - 1);
    char * found_divisor;
    found_divisor = malloc(LIMIT * sizeof(char));
    if (found_divisor == NULL) {
        printf("Error on malloc");
        return -1;
    }
    unsigned long long i;
    unsigned long long trial_div;
    unsigned long long multiple;
    unsigned long long sum = 0;

    for (i = 0; i < LIMIT; ++i) {
        found_divisor[i] = 0;
    }

    for (trial_div = 2; trial_div <= ROOT_LIMIT; ++trial_div) {
        if (found_divisor[trial_div] == 0) {
            for (multiple = trial_div * trial_div; multiple < LIMIT; multiple += trial_div) {
                if (found_divisor[multiple] == 0) {
                    found_divisor[multiple] = 1;
                    sum += trial_div;
                }
            }
        }
    }

    for (i = 2; i < LIMIT; ++i) {
        if (found_divisor[i] == 0) {
            sum += i;
        }
    }

    free(found_divisor);
    printf("%lld\n", sum);
    return 0;
}

1
Якщо пам'ять викликає занепокоєння, одного біта на номер має бути достатньо. Ви можете використовувати біт-маску для зберігання прапорів.
Рето Коради

@RetoKoradi На жаль, це, мабуть, уповільнить програму достатньо, щоб поставити її на 1 хвилину.
isaacg

Для чого вам потрібен assert.h?
Макс Рід

@MaxRied Це було залишено з ранньої версії.
isaacg

3

Perl, факторинг грубої сили

use ntheory ":all";
sub T {
  my $sum=0;
  for (1..$_[0]) {
    $sum += !($_%2) ? 2 : !($_%3) ? 3 : !($_%5) ? 5 : (factor($_))[0];
  }
  $sum
}
T(90_000_000);

Я можу отримати близько 9е7 за 25 секунд на своїй машині Linux. Це може бути швидше, перекопавшись до коду С, як це говориться після перевірки на 2/3/5, повністю розподілити число.

Є набагато розумніші способи зробити це за допомогою просіювання. Я думав, що простий спосіб грубої сили стане початком. В основному це проблема 521 Проекту Ейлера.


Якщо це взагалі корисно знати, в Python з ситом я можу керувати лише T (47000). Я спробую щось подібне до того, що ви робите, щоб побачити, чи швидше це.
Каде

Схоже, не використовувати сито швидше .. Я зміг розрахувати T (493900) аналогічним вашому методу.
Каде

Ніколи раніше не використовував Perl, але мені вдалося перевірити вашу відповідь, я додам вас до списку!
Nicolás Siplis

Якщо чесно, для цього використовується мій модуль, який робить факторинг в C (ви можете змусити його використовувати чистий Perl для всього, але, звичайно, це не так швидко).
DanaJ

Відповідь можна обчислити, використовуючи будь-яку комбінацію мов, тож це нормально.
Nicolás Siplis

3

Іди, 21е9

Чи робить сито для знаходження мінімального коефіцієнта кожного числа <= N. Породжує горотини для підрахунку ділянок простору чисел.

Запустіть з "go run run prime.go -P 4 -N 21000000000".

package main

import (
    "flag"
    "fmt"
    "runtime"
)

const S = 1 << 16

func main() {
    var N, P int
    flag.IntVar(&N, "N", 10000, "N")
    flag.IntVar(&P, "P", 4, "number of goroutines to use")
    flag.Parse()
    fmt.Printf("N = %d\n", N)
    fmt.Printf("P = %d\n", P)
    runtime.GOMAXPROCS(P)

    // Spawn goroutines to check sections of the number range.
    c := make(chan uint64, P)
    for i := 0; i < P; i++ {
        a := 2 + (N-1)*i/P
        b := 2 + (N-1)*(i+1)/P
        go process(a, b, c)
    }
    var sum uint64
    for i := 0; i < P; i++ {
        sum += <-c
    }
    fmt.Printf("T(%d) = %d\n", N, sum)
}

func process(a, b int, res chan uint64) {
    // Find primes up to sqrt(b).  Compute starting offsets.
    var primes []int
    var offsets []int
    for p := 2; p*p < b; p++ {
        if !prime(p) {
            continue
        }
        primes = append(primes, p)
        off := a % p
        if off != 0 {
            off = p - off
        }
        offsets = append(offsets, off)
    }

    // Allocate sieve array.
    composite := make([]bool, S)

    // Check factors of numbers up to b, a block of S at a time.
    var sum uint64
    for ; a < b; a += S {
        runtime.Gosched()
        // Check divisibility of [a,a+S) by our set of primes.
        for i, p := range primes {
            off := offsets[i]
            for ; off < S; off += p {
                if composite[off] {
                    continue // Divisible by a smaller prime.
                }
                composite[off] = true
                if a+off < b {
                    sum += uint64(p)
                }
            }
            // Remember offset for next block.
            offsets[i] = off - S
        }
        // Any remaining numbers are prime.
        for i := 0; i < S; i++ {
            if composite[i] {
                composite[i] = false // Reset for next block.
                continue
            }
            if a+i < b {
                sum += uint64(a + i)
            }
        }
    }
    res <- sum
}

func prime(n int) bool {
    for i := 2; i*i <= n; i++ {
        if n%i == 0 {
            return false
        }
    }
    return true
}

Зауважте, що відповідь на N = 21e9 знаходиться в межах від 2 ^ 63 до 2 ^ 64, тому для правильного підрахунку мені довелося використовувати непідписані 64-бітні вставки ...


Мені довелося змінити його, щоб він працював на моїй машині (зменшив N до 1e9), але сам час роботи досить швидкий, хороша робота!
Nicolás Siplis

@ NicolásSiplis: використання пам'яті виправлено.
Кіт Рендалл

Час виконання становило 80 секунд, але 1.6e10 було розраховано майже рівно за 60!
Nicolás Siplis

2

C ++, 1 << 34 ~ 1.7e10

Intel(R) Core(TM) i5-2410M CPU @ 2.30GHz

$ g++ -O2 test3.cpp 
$ time ./a.out 
6400765038917999291

real    0m49.640s
user    0m49.610s
sys 0m0.000s
#include <iostream>
#include <vector>

using namespace std;

const long long root = 1 << 17; // must be a power of two to simplify modulo operation
const long long rootd2 = root >> 1;
const long long rootd2m1 = rootd2 - 1;
const long long mult = root; // must be less than or equal to root
const long long n = root * mult; // unused constant (function argument)

int main() {
  vector < int > sieve(rootd2, 0);
  vector < int > primes;
  vector < long long > nexts;
  primes.reserve(root);
  nexts.reserve(root);
  // initialize sum with result for even numbers
  long long sum = n / 2 * 2;
  // sieve of Erathosthenes for numbers less than root
  // all even numbers are skipped
  for(long long i = 1; i < rootd2; ++i){
    if(sieve[i]){
      sieve[i] = 0;
      continue;
    }
    const long long val = i * 2 + 1;
    primes.push_back(val);
    sum += val;
    long long j;
    for(j = (val + 1) * i; j < rootd2; j += val){
      sum += val * (1 - sieve[j]); // conditionals replaced by multiplication
      sieve[j] = 1;
    }
    nexts.push_back(j);
  }
  int k = primes.size();
  long long last = rootd2;
  // segmented sieve of Erathosthenes
  // all even numbers are skipped
  for(int segment = 2; segment <= mult; ++segment){
    last += rootd2;
    for(int i = 0; i < k; ++i){
      long long next = nexts[i];
      long long prime = primes[i];
      if(next < last){
        long long ptr = next & rootd2m1; // modulo replaced by bitmasking
        while(ptr < rootd2){
          sum += prime * (1 - sieve[ptr]); // conditionals replaced by multiplication
          sieve[ptr] = 1;
          ptr += prime;
        }
        nexts[i] = (next & ~rootd2m1) + ptr;
      }
    }
    for(int i = 0; i < rootd2; ++i){
      sum += ((segment - 1) * root + i * 2 + 1) * (1 - sieve[i]);
      sieve[i] = 0;
    }
  }
  cout << sum << endl;
}

2

Java 8: 1,8е8 2,4е8

Цей запис не порівнюється з кількома іншими, які вже були, але я хотів опублікувати свою відповідь, оскільки мені було цікаво працювати над цим.

Основні оптимізації мого підходу такі:

  • Кожне парне число має найменший коефіцієнт 2, тому їх можна додавати безкоштовно після опрацювання кожного непарного числа. В основному, якщо ви виконали роботу, щоб обчислити T(N)коли N % 2 == 1, ви це знаєте T(N + 1) == T(N) + 2. Це дозволяє мені почати рахувати з трьох і збільшити по ітерації на двійки.
  • Я зберігаю свої прості номери в масиві на відміну від Collectionтипу. Це більше ніж удвічі збільшив Nя.
  • Я використовую прості числа, щоб підрахувати число, а не виконувати сито Ератосфена. Це означає, що моє зберігання пам’яті майже повністю обмежене моїм масивом прайметів.
  • Я зберігаю квадратний корінь числа, для якого я намагаюся знайти найменший множник. Я намагався @ user1354678 підходити кожен раз, коли потрібно простежувати основний фактор, але це коштувало мене приблизно в 1е7 від моєї оцінки.

Ось про все, що там є. Мій код повторюється від 3 до двох, поки він не виявить, що він потрапив або перевищив обмеження часу, і в цей момент він виписує відповідь.

package sum_of_smallest_factors;

public final class SumOfSmallestFactors {
    private static class Result {
        private final int number;
        int getNumber() {
            return number;
        }

        private final long sum;
        long getSum() {
            return sum;
        }


        Result(int number, long sum) {
            this.number = number;
            this.sum = sum;
        }
    }


    private static final long TIME_LIMIT = 60_000_000_000L; // 60 seconds x 1e9 nanoseconds / second


    public static void main(String[] args) {
        SumOfSmallestFactors main = new SumOfSmallestFactors();
        Result result = main.run();
        int number = result.getNumber();
        long sum = result.getSum();
        System.out.format("T(%,d) = %,d\n", number, sum);
    }


    private int[] primes = new int[16_777_216];
    private int primeCount = 0;
    private long startTime;


    private SumOfSmallestFactors() {}

    private Result run() {
        startClock();
        int number;
        long sumOfSmallestFactors = 2;
        for (number = 3; mayContinue(); number += 2) {
            int smallestFactor = getSmallestFactor(number);
            if (smallestFactor == number) {
                addPrime(number);
            }
            sumOfSmallestFactors += smallestFactor + 2;
        }
        --number;

        Result result = new Result(number, sumOfSmallestFactors);
        return result;
    }

    private void startClock() {
        startTime = System.nanoTime();
    }

    private boolean mayContinue() {
        long currentTime = System.nanoTime();
        long elapsedTime = currentTime - startTime;
        boolean result = (elapsedTime < TIME_LIMIT);
        return result;
    }

    private int getSmallestFactor(int number) {
        int smallestFactor = number;
        int squareRoot = (int) Math.ceil(Math.sqrt(number));

        int index;
        int prime = 3;
        for (index = 0; index < primeCount; ++index) {
            prime = primes[index];

            if (prime > squareRoot) {
                break;
            }

            int remainder = number % prime;
            if (remainder == 0) {
                smallestFactor = prime;
                break;
            }
        }

        return smallestFactor;
    }

    private void addPrime(int prime) {
        primes[primeCount] = prime;
        ++primeCount;
    }
}

Працюючи в іншій системі (Windows 8.1, ядро ​​Intel i7 @ 2,5 ГГц, 8 ГБ оперативної пам’яті) з останньою версією Java 8, помітно кращі результати без змін коду:

T(240,308,208) = 1,537,216,753,010,879

Якби ви могли замінити mayContinue()в for loop conditionтільки з умовою простого, можна домогтися більш високого результату. І мені подобається ваш спосіб попереднього підрахунку рівних сум, а потім збільшення на два.
Кодер

@ user1354678, Дякую за рекомендацію. Як не дивно, не вийшло. Я спробував варіанти цього коду на іншому комп'ютері і виявив, що розміщена версія є найшвидшою. Видалення дзвінків годинника з коду та використання простого номера порогу коштувало мені трохи більше секунди. Я навіть спробував переключити мою startTimeна а, endTimeщоб усунути віднімання ~ 2e7, але це коштувало мені 3e7 з моєї оцінки!
sadakatsu

Ви спробували це System.nanoTime() - startTime < TIME_LIMIT , тому що він трохи пришвидшує ваш код для мене. Це не надзвичайно швидко, враховуючи факт, цей стан перевіряється мільйони разів, це буде трохи швидко. Одне, що я дізнався з вашого коду, це не вводити forвсередину for.. Після переходу forдо іншого методу в моєму коді швидкість мого коду збільшується на 40%. є набагато ефективнішими, ніж ArrayList, якщо врахувати той факт, що він дістався мільйони разів ..
Кодер

Ви можете досягти x2результату, якщо реалізуєте MultiThreading. Але потрібно заздалегідь розрахувати весь масив перед тим, як запустити обчислення Prime.
Кодер

@ user1354678, переміщення чека з mayContinue()методу в цикл for for коштує мені 8e6 від моєї оцінки. Це може бути проблемою локальних оптимізацій. Коли я розробляв це рішення, я експериментував з декількома типами даних для зберігання прайменів. Мені вдалося досягти лише 8,8e7 за допомогою ArrayList, але я натиснув 1,8e8 (зараз 2,4e8) за допомогою масиву. Можливо, деякі підсилення продуктивності пов'язані з пошуком, але є певні стимули для розподілу пам'яті. Я подумав про алгоритм багатопотокової роботи, але я зіткнувся з проблемами.
sadakatsu

1

R, 2,5e7

Просте розумне сито Ератостена, максимально векторизоване. R насправді не розроблений для подібних проблем, і я впевнений, що його можна зробити швидше.

MAX <- 2.5e7
Tot <- 0
vec <- 2:MAX 
while(TRUE) {
    if (vec[1]*vec[1] > vec[length(vec)]) {
        Tot <- Tot + sum(as.numeric(vec))
        break
    }

    fact <- which(vec %% vec[1] == 0)
    Tot <- Tot + vec[1]*length(vec[fact])
    vec <- vec[-fact]
}
Tot

Справедлива точка про Т. 2: MAX - вектор цілих чисел, тому для великих значень MAX sum(vec)приводить до цілого переповнення і повертає NA. sum(as.numeric(vec))підсумовує вектор пар, який не переповнює (хоча це може не дати правильної відповіді)
mawir

1

Пітон, ~ 7e8

Використання додаткового сита Ератостена. Потрібно бути обережним, щоб позначене значення було позначене найнижчим дільником, але в іншому випадку реалізація є досить прямою.

Час прийнято за допомогою PyPy 2.6.0, вхід прийнято як аргумент командного рядка.

from sys import argv
from math import sqrt

n = int(argv[1])
sieve = {}
imax = int(sqrt(n))

t = n & -2
i = 3
while i <= n:
  divs = sieve.pop(i, [])
  if divs:
    t += divs[-1]
    for v in divs:
      sieve.setdefault(i+v+v, []).append(v)
  else:
    t += i
    if i <= imax: sieve[i*i] = [i]
  i += 2

print t

Використання зразка

$ pypy sum-lpf.py 10000
5786451

$ pypy sum-lpf.py 100000000
279218813374515

0

Юлія, 5е7

Звичайно, Юлія може зробити краще, але це те, що я маю на даний момент. Це робить 5e7 за 60 секунд на JuliaBox, але я ще не можу перевірити локально. Сподіваюся, до цього часу я подумав про більш розумний підхід.

const PRIMES = primes(2^16)

function lpf(n::Int64)
    isprime(n) && return n
    for p in PRIMES
        n % p == 0 && return p
    end
end

function T(N::Int64)
    local x::Int64
    x = @parallel (+) for i = 2:N
        lpf(i)
    end
    x
end

Тут ми створюємо функцію lpf яка повторюється через послідовні праймери та перевіряє вхід на подільність кожного. Функція повертає перший ділиться, тим самим отримуючи найменший простий коефіцієнт.

Основна функція обчислює lpfцілі числа від 2 до входу паралельно і зменшує результат шляхом підсумовування.


0

Лист звичайний, 1е7

(defvar input 10000000)
(defvar numbers (loop for i from 2 to input collect i))
(defvar counter)
(defvar primes)

(setf primes (loop for i from 2 to (floor (sqrt input))
    when (loop for j in primes
        do (if (eq (mod i j) 0) (return nil))
        finally (return t))
    collect i into primes
    finally (return primes)))

(format t "~A~%"    
    (loop for i in primes
        do (setf counter 0)
        summing (progn (setf numbers (remove-if #'(lambda (x) (if (eq (mod x i) 0) (progn (incf counter) t))) numbers))
                (* i counter)) into total
        finally (return (+ total (reduce #'+ numbers)))))

Я вирішив спершу створити список простих чисел від 2 до (sqrt input) , а потім протестувати кожне значення з простими числами, тоді як раніше я би перевіряв кожне число до(sqrt input) , що було б безглуздо (наприклад, якщо число ділиться на 4, це також ділиться на 2, тому це вже враховується.)

Слава Богу за побічні ефекти, поки я на це. Видалення, якщо обидва зменшує розмір списку і підраховує, скільки елементів було вилучено, тож я просто мушу помножити це на будь-яке значення, в якому знаходиться цикл, і додати його до поточної сукупності.

(Веселий факт: deleteце руйнівний еквівалент remove, але з будь-якої причини deleteвідбувається на всі види повільніше, ніж removeу цьому випадку.)


Ніколи раніше не використовував Lisp, я отримую помилку компілятора при спробі запуску вашого коду: (defvar total 0) (defvar counter 0) (defvar input 10000) (номера defvar (цикл для i від 2 для введення collection i)) ( цикл для i від 2 до (пол (вхід sqrt)) (setf counter 0) підсумовування (prog2 (nsupstitute-if 0 # '(lambda (x) (if (eq (mod xi) 0) (progn (incf counter) t ))) числа) (* лічильник) (встановити номери (видалити 0 чисел))) остаточно підсумову (повернення (+ загальна кількість (зменшити # '+ числа))))
Nicolás Siplis

Я використовую SBCL 1.0.38, але коли прийду додому, я оновлю до останньої версії та побачу, як це відбувається. Якщо ви збережете його у файл, ви можете запустити його з "sbcl --script <ім'я файлу>".
Свічки

Я намагався, але все одно не пощастило, на всякий випадок я спробував компілювати в Інтернеті з Ideone, але це теж не вийшло.
Nicolás Siplis

О, вибачте, я забув ключове слово "робити" у рядку 6. Хоча він має працювати зараз, хоча б ще один знімок.
Свічки

Чудово, він обчислює 6e6 за 60 секунд на моїй машині! До речі, якщо я вирішу ввести власний код, чи знаєте ви, чи слід подавати його як відповідь? Я не впевнений, чи дозволять це нові подання.
Nicolás Siplis

0

Іржа 1,5е9

Дуже наївне сито Ератостена, але я відчував, що Руст не отримав тут любові!

// Expected (approximate) number of primes
fn hint(n:usize) -> usize {
    if n < 2 { 
        1
    } else {
        n / ((n as f64).ln() as usize) + 1
    }
}

fn main() {
    let n:usize = match std::env::args().nth(1) {
        Some(s) => s.parse().ok().expect("Please enter a number !"),
        None => 10000,
    };
    let mut primes = Vec::with_capacity(hint(n));
    let mut sqrt = 2;
    let s = (2..).map(|n:u32| -> u32 {
        if (sqrt * sqrt) < n {
            sqrt += 1;
        }
        let (div, unseen) = match primes.iter().take_while(|&p| *p <= sqrt).filter(|&p| n % p == 0).next() {
            Some(p) => (*p, false),
            None => (n, true),
        };
        if unseen {
            primes.push(div);
        }
        div
    }).take(n-1).fold(0, |acc, p| acc + p);
    println!("{}", s);
}

0

Java 2.14e9

Чисте сито Ератостена з перевагою BitSet

Я вирахував Найменший першорядним фактором суму ДО Integer.MAX_VALUE - 1тільки в 33.89 s. Але я не можу продовжувати більше, тому що будь-яке подальше призводить до переповнення цілочисельних розмірів на розмір бітсета. Тому я працюю над створенням ще одного бітсета для наступного набору діапазонів. До цього часу це найшвидше, що я можу створити.


T(214,74,83,646) = 109931450137817286 in 33.89 s
aka
T(2,147,483,646) = 109931450137817286 in 33.89 s

import java.util.BitSet;

public class SmallPrimeFactorSum {

    static int    limit     = Integer.MAX_VALUE - 1;

    // BitSet is highly efficient against boolean[] when Billion numbers were involved
    // BitSet uses only 1 bit for each number
    // boolean[] uses 8 bits aka 1 byte for each number which will produce memory issues for large numbers
    static BitSet primes    = new BitSet(limit + 1);
    static int    limitSqrt = (int) Math.ceil(Math.sqrt(limit));

    static long   start     = System.nanoTime();

    static long   sum       = 0;

    public static void main(String[] args) {
        genPrimes();
    }

    // Generate Primes by Sieve of Eratosthenes
    // Sieve of Eratosthenes is much efficient than Sieve of Atkins as
    // Sieve of Atkins involes Division, Modulus, Multiplication, Subtraction, Addition but
    // Sieve of Eratosthenes involves only addition
    static void genPrimes() {

        // Inverse the Bit values
        primes.flip(0, limit + 1);

        // Now all Values in primes will now be true,
        // True  represents     prime number 
        // False represents not prime number

        // Set 0 and 1 as not Prime number
        primes.clear(0, 2);

        // Set all multiples of each Prime as not Prime;
        for ( int prime = 2; prime > 0 && prime <= limit && prime > 0; prime = primes.nextSetBit(prime + 1) ) {
            // Add Current Prime as its Prime factor
            sum += prime;
            // Skip marking if Current Prime > SQRT(limit)
            if ( prime > limitSqrt ) {
                continue;
            }
            // Mark Every multiple of current Prime as not Prime
            for ( int multiple = prime + prime; multiple <= limit && multiple > 0; multiple += prime ) {
                // Mark as not Prime only if it's true already
                if ( primes.get(multiple) ) {
                    // Add Current Prime as multiple's Prime factor
                    sum += prime;
                    primes.clear(multiple);
                }
            }
        }

        System.out.printf("T(%d) = %d in %.2f s", limit, sum, (System.nanoTime() - start) / 1000000000.0);
        //System.out.printf("Total Primes upto %d : %d\n", limit, primes.cardinality());
    }

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.