ผลรวมของปัจจัยสำคัญที่เล็กที่สุด


19

SF (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

ผู้ชนะจะเป็นผู้ที่จัดการคำนวณ T (N) ที่ใหญ่ที่สุดใน 60 วินาทีบนแล็ปท็อปของฉัน (Toshiba Satellite L845, Intel Core i5, RAM 8GB)


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

Pf (2) = 2, Pf (3) = 3, ดังนั้น, T (3) = 2 + 3 = 5. ฉันถูกไหม? ฉันตั้งโปรแกรมให้ค้นหาปัจจัยสำคัญ แต่คุณช่วยอธิบายเกี่ยวกับข้อกำหนดปัจจุบันได้ไหม ขอบคุณ
The Coder

1
@ToddLehman ฉันใช้รหัสในแล็ปท็อปของฉันเอง (Sony Vaio SVF14A16CLB) ดังนั้นหากใช้เวลาน้อยกว่า 60 วินาทีฉันจะเพิ่มจำนวนและลดลงเมื่อใช้เวลานานกว่า
Nicolás Siplis

1
ใช่ตราบใดที่มันรันบนเครื่องของฉันเองและส่งคำตอบที่ถูกต้องภายใน 60 วินาทีหรือน้อยกว่านั้นก็เป็นที่ยอมรับได้
Nicolás Siplis

1
มันมี 4 กระทู้
Nicolás Siplis

1
อนุญาตให้ใช้ไลบรารีของบุคคลที่สามหรือไม่ มันก็โอเคถ้าโปรแกรมกำลังสร้างเธรด?
Coder

คำตอบ:


12

นิมิต, 3.6e13

การกรองเพียงอย่างเดียวไม่ใช่คำตอบที่ดีที่สุดเมื่อพยายามคำนวณค่า 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 นิยาม Procs แบบนิพจน์กลับมาโดยอัตโนมัติ
kirbyfan64sos

3
นี่ไม่ใช่รหัสแรกที่เร็วที่สุดที่ Nim ได้รับมาร์จิ้นที่เห็นได้ชัด อาจคุ้มค่าที่จะตรวจสอบ
โม่

ฉันอยากรู้ว่ามันทำงานอย่างไรเมื่อใช้ GMP แต่ไม่สามารถใช้งานได้อย่างถูกต้องแม้จะมีความพยายามของฉัน
Nicolás Siplis

นิ่มกำลังทำรายการเรียนรู้ของฉัน!
Sp3000

5

C, Prime Sieve: 5e9

ผล:

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

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

โปรแกรม:

ในขณะที่มันเป็นโปรแกรมที่ค่อนข้างตรงไปตรงมาฉันใช้เวลาสักครู่ในการหาวิธีการจัดการหน่วยความจำที่ถูกต้อง - ฉันมี RAM เพียงพอเพียง 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
หากหน่วยความจำเป็นปัญหาหนึ่งบิตต่อหนึ่งหมายเลขควรจะเพียงพอ คุณสามารถใช้ bitmask เพื่อเก็บค่าสถานะ
Reto Koradi

@RetoKoradi แต่น่าเสียดายที่นั่นอาจทำให้โปรแกรมช้าลงพอที่จะวางเครื่องหมาย 1 นาที
isaacg

คุณต้องการ assert.h เพื่ออะไร
Max Ried

@ MaxRied มันเหลือจากรุ่น earlie
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);

ฉันสามารถไปถึง 9e7 ใน 25 วินาทีบนเครื่อง Linux ของฉัน มันอาจจะเร็วขึ้นโดยการขุดลงในรหัส C เนื่องจากมันบอกว่าหลังจากตรวจสอบ 2/3/5 แล้วให้คำนึงถึงจำนวนทั้งหมด

มีวิธีที่ฉลาดกว่านี้มากในการทำเช่นนี้โดยใช้การกรอง ฉันคิดว่าวิธีเดรัจฉานบังคับง่าย ๆ น่าจะเป็นการเริ่มต้น นี่เป็นปัญหาของโครงการออยเลอร์ 521 โดยวิธี


ถ้าเป็นประโยชน์ที่จะรู้ใน Python พร้อมตะแกรงฉันสามารถจัดการ T (47000) ได้เท่านั้น ฉันจะลองทำสิ่งที่คล้ายกับสิ่งที่คุณทำเพื่อดูว่าเร็วขึ้นหรือไม่
Kade

ดูเหมือนว่าไม่ได้ใช้ตะแกรงเร็วกว่า .. ฉันสามารถคำนวณ T (493900) ด้วยวิธีที่คล้ายกับของคุณได้
Kade

ไม่เคยใช้ Perl มาก่อน แต่ฉันจัดการเพื่อยืนยันคำตอบของคุณฉันจะเพิ่มคุณในรายการ!
Nicolás Siplis

เพื่อความเป็นธรรมนี้ใช้โมดูลของฉันที่ทำแฟคตอริ่งใน C (คุณสามารถบังคับให้ใช้ Perl บริสุทธิ์สำหรับทุกสิ่ง แต่แน่นอนว่ามันไม่เร็วเท่านี้)
DanaJ

คำตอบนั้นสามารถคำนวณได้โดยใช้การรวมกันของภาษาใด ๆ ดังนั้นก็โอเค
Nicolás Siplis

3

ไป 21e9

ไม่ตะแกรงเพื่อค้นหาปัจจัยขั้นต่ำของแต่ละหมายเลข <= N วางไข่ goroutines เพื่อนับส่วนของพื้นที่หมายเลข

เรียกใช้ด้วย "go 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 ดังนั้นฉันต้องใช้ int 64- บิตที่ไม่ได้ลงชื่อเพื่อนับอย่างถูกต้อง ...


ฉันต้องแก้ไขมันเพื่อให้ทำงานบนเครื่องของฉัน (ลดลง N เป็น 1e9) แต่ตัวรันไทม์นั้นค่อนข้างเร็วใช้งานได้ดี!
Nicolás Siplis

@ NicolásSiplis: การใช้งานหน่วยความจำได้รับการแก้ไข
Keith Randall

รันไทม์คือ 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.8e8 2.4e8

รายการนี้ไม่ได้เปรียบเทียบกับคนอื่น ๆ หลายคนแล้ว แต่ฉันต้องการที่จะโพสต์คำตอบของฉันเพราะฉันสนุกกับการทำงานนี้

การเพิ่มประสิทธิภาพหลักของวิธีการของฉันมีดังนี้:

  • เลขคู่ทุกตัวมีตัวคูณที่เล็กที่สุด 2 ดังนั้นสามารถเพิ่มได้ฟรีหลังจากประมวลผลทุกเลขคู่ โดยทั่วไปถ้าคุณได้ทำผลงานในการคำนวณT(N)เมื่อคุณรู้ว่าN % 2 == 1 T(N + 1) == T(N) + 2สิ่งนี้ทำให้ฉันเริ่มนับที่สามและเพิ่มขึ้นซ้ำสองครั้ง
  • ฉันเก็บหมายเลขเฉพาะของฉันไว้ในอาร์เรย์เมื่อเทียบกับCollectionประเภท นี่มากกว่าสองเท่าที่Nฉันสามารถเข้าถึงได้
  • ฉันใช้ตัวเลขเฉพาะเพื่อแยกแยะตัวเลขแทนการแสดง Sieve of Eratosthenes ซึ่งหมายความว่าที่เก็บข้อมูลหน่วยความจำของฉันถูก จำกัด เกือบทั้งหมดในอาร์เรย์ช่วงเวลาของฉัน
  • ฉันเก็บสแควร์รูทของจำนวนที่ฉันพยายามหาปัจจัยที่เล็กที่สุด ฉันได้ลองวิธีการของ @ user1354678 ในการหาปัจจัยสำคัญในแต่ละครั้ง แต่สิ่งนี้ทำให้ฉันเสียค่าใช้จ่ายประมาณ 1e7 จากคะแนนของฉัน

นั่นคือทั้งหมดที่เกี่ยวกับมัน รหัสของฉันวนซ้ำจาก 3 วันโดย twos จนกว่าจะตรวจพบว่ามีการกดหรือเกินขีด จำกัด เวลา ณ จุดนั้นมันจะแยกคำตอบออก

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 core i7 @ 2.5 GHz, 8 GB RAM) ด้วย Java 8 รุ่นล่าสุดมีผลลัพธ์ที่ดีกว่าอย่างเห็นได้ชัดโดยไม่มีการเปลี่ยนแปลงรหัส:

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

หากคุณสามารถแทนที่mayContinue()ในfor loop conditionมีเพียงเงื่อนไขง่ายคุณสามารถบรรลุผลที่สูงขึ้น และฉันชอบวิธีของคุณในการคำนวณผลรวมแม้กระทั่งเพิ่มขึ้นสองเท่า
Coder

@ user1354678 ขอขอบคุณสำหรับคำแนะนำ แปลกมันใช้งานไม่ได้ ฉันลองชุดรูปแบบของรหัสนี้ในคอมพิวเตอร์เครื่องอื่นและพบว่ารุ่นที่โพสต์นั้นเป็นรุ่นที่เร็วที่สุด การลบการโทรออกจากรหัสและการใช้หมายเลข threshhold ง่ายทำให้ฉันเสียค่าใช้จ่ายเล็กน้อยในหนึ่งวินาที ฉันได้ลองเปลี่ยนของฉันstartTimeเป็น a endTimeเพื่อกำจัดการลบ ~ 2e7 แต่นั่นทำให้ฉันต้องเสีย 3e7 จากคะแนนของฉัน!
sadakatsu

คุณลองด้วยSystem.nanoTime() - startTime < TIME_LIMITหรือไม่เพราะมันกระชับรหัสของคุณให้ฉันหน่อย มันไม่เร็วอย่างเห็นได้ชัดเมื่อพิจารณาความจริงแล้วสภาพนี้มีการตรวจสอบหลายล้านครั้งมันจะเร็วไปหน่อย สิ่งหนึ่งที่ฉันเรียนรู้จากโค้ดของคุณคืออย่าใส่forข้างในfor.. หลังจากย้ายforไปยังวิธีอื่นในโค้ดของฉันความเร็วโค้ดของฉันเพิ่มขึ้น 40% ขอบคุณ .. สิ่งหนึ่งที่ฉันยังคงหาคือคืออาร์เรย์ไม่หรือ จะมีประสิทธิภาพกว่า ArrayList มากเมื่อพิจารณาข้อเท็จจริงที่ว่ามันเป็นความจริงล้านครั้ง ..
Coder

คุณสามารถบรรลุผลถ้าคุณใช้x2 MultiThreadingแต่จะต้องคำนวณล่วงหน้าทั้งอาร์เรย์ก่อนเรียกใช้การคำนวณแบบ Prime
Coder

@ user1354678 การย้ายการตรวจสอบจากmayContinue()วิธีการในการวนค่าใช้จ่ายฉัน 8e6 จากคะแนนของฉัน นี่อาจเป็นปัญหาของการปรับแต่งเฉพาะที่ ฉันทดลองกับข้อมูลหลายชนิดเพื่อจัดเก็บช่วงเวลาที่ฉันพัฒนาโซลูชันนี้ ฉันสามารถไปถึง 8.8e7 ด้วยArrayListแต่ฉันกด 1.8e8 (ตอนนี้ 2.4e8) โดยใช้อาร์เรย์ อาจมีการเพิ่มประสิทธิภาพบางอย่างที่เกี่ยวข้องกับการค้นหา แต่มีการเพิ่มประสิทธิภาพที่แน่นอนสำหรับการจัดสรรหน่วยความจำ ฉันคิดเกี่ยวกับอัลกอริทึมแบบมัลติเธรด แต่ฉันพบปัญหา
sadakatsu

1

R, 2.5e7

ตะแกรงที่เรียบง่ายที่มีใจรักของ Eratosthenes, vectorised ให้มากที่สุด 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

จุดประสงค์เกี่ยวกับ T. 2: MAX เป็นเวกเตอร์ของจำนวนเต็มดังนั้นสำหรับค่าขนาดใหญ่ของ MAX sum(vec)จะนำไปสู่การล้นจำนวนเต็มและส่งคืน NA sum(as.numeric(vec))เป็นข้อสรุปเวกเตอร์ของคู่ที่ไม่ล้น (แม้ว่ามันอาจจะไม่ให้คำตอบที่ถูกต้อง)
mawir

1

Python ~ 7e8

ใช้ตะแกรงที่เพิ่มขึ้นของ Erathostenes การดูแลบางอย่างจำเป็นต้องดำเนินการว่าค่าที่ทำเครื่องหมายไว้นั้นถูกทำเครื่องหมายด้วยตัวหารต่ำสุด แต่การดำเนินการนั้นค่อนข้างตรงไปตรงมา

กำหนดเวลาถ่ายด้วย 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

Julia, 5e7

แน่นอนจูเลียทำได้ดีกว่านี้ แต่นี่คือสิ่งที่ฉันมีในตอนนี้ นี่ใช้เวลา 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

เสียงกระเพื่อมสามัญ, 1e7

(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 ทั้งหมด 0) (defvar counter 0) (อินพุต defvar 10000) (หมายเลข defvar (วนสำหรับ i จาก 2 ถึงอินพุตรวบรวม i)) ( วนซ้ำสำหรับ i จาก 2 ถึง (floor (อินพุต sqrt)) (setf ตัวนับ 0) การรวม (prog2 (nsubstitute-if 0 # '(lambda (x) (ถ้า (eq (mod xi) 0)) (progn (incf counter) t ))) ตัวเลข) (* i counter) (ตัวเลข setf (ลบ 0 ตัวเลข))) รวมเป็นที่สุด (return (+ total (ลด # '+ numbers))))
Nicolás Siplis

ฉันใช้ SBCL 1.0.38 แต่เมื่อฉันกลับถึงบ้านฉันจะอัปเดตเป็นเวอร์ชันล่าสุดและดูว่ามันจะไปอย่างไร หากคุณบันทึกลงในไฟล์คุณสามารถรันด้วย "sbcl --script <filename>"
เทียน

ฉันพยายาม แต่ก็ยังไม่มีโชคในกรณีที่ฉันพยายามรวบรวมออนไลน์ด้วย Ideone แต่ก็ไม่ได้ผล
Nicolás Siplis

โอ้ขอโทษฉันลืมคำว่า "ทำ" ในบรรทัดที่ 6 มันควรจะทำงานตอนนี้แม้ว่าจะให้อีกนัด
เทียน

เยี่ยมมากมันคำนวณ 6e6 ใน 60 วินาทีบนเครื่องของฉัน! อย่างไรก็ตามถ้าฉันตัดสินใจที่จะใส่รหัสของตัวเองคุณรู้หรือไม่ว่าฉันควรส่งมันเป็นคำตอบ? ฉันไม่แน่ใจว่าจะอนุญาตการส่งใหม่หรือไม่
Nicolás Siplis

0

สนิม 1.5e9

Eratosthene ที่ไร้เดียงสามาก แต่ฉันรู้สึกว่า Rust ไม่ได้รับความรักใด ๆ ที่นี่!

// 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

ตะแกรงบริสุทธิ์ของ Eratosthenes พร้อมประโยชน์จาก BitSet

ผมได้คำนวณผลรวมขนาดเล็กที่สุดในปัจจัยที่นายกรัฐมนตรีไม่เกินเพียงแค่ในInteger.MAX_VALUE - 1 33.89 sแต่ฉันไม่สามารถดำเนินการใด ๆ ที่มีขนาดใหญ่กว่าได้อีกเพราะจะนำไปสู่การรวมตัวเกินจำนวนเต็มบนขนาดของบิตเซ็ต ดังนั้นฉันจึงพยายามสร้าง Bitset อีกชุดสำหรับ Ranges ชุดถัดไป จนถึงตอนนี้เป็นวิธีที่เร็วที่สุดที่ฉันสามารถสร้างได้


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());
    }

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.