2016-05-17 11 views
0

Die schnellste Methode zur Generierung von Primzahlen in Python ist primesieve-python, eine Bindung an die C/C++ Bibliothek primesieve (http://primesieve.org/). Sie können über Primzahlen mitWie können wir die Primesieve C-Bibliothek von Julia nennen?

import primesieve 
it = primesieve.Iterator() 
prime = it.next_prime() 
prime = it.next_prime() 

iterieren wir diese Python-Bibliothek von Julia PyCall mit aufrufen können. Ich erwarte jedoch, dass es schneller wäre, über Primzahlen zu iterieren, indem die C-Funktion primesieve_next_prime direkt aufgerufen wird. Wie einfach ist das, für Leute ohne C-Kenntnisse?

+3

Haben Sie versucht, diesen Teil der Dokumentation folgen? http://docs.julaulang.org/en/release-0.4/manual/calling-c-and-fortran-code/ – Chisholm

+0

Das war meine erste Anlaufstelle. Aber ich konnte die Beispiele nicht so einfach an die Definition anpassen: http://primesieve.org/api/primesieve__iterator_8h.html#a4512ff6ce19e25042f07fe1337bb0dbe Ich hatte den Eindruck, dass - im Gegensatz zu Julia im Allgemeinen - der Aufruf von C-Code etwas für erfahrene Programmierer war. – u003f

+0

Die Erstellung eines Wrappers für eine C-API von Julia ist ziemlich einfach, und es ist nicht nur für "erfahrene" Programmierer (Sie wissen nicht einmal wirklich C, um es zu tun, in den meisten Fällen müssen Sie nur verstehen, was die äquivalenten Julia Typen sind). Welche Teile des Umbruchs der C-API gab Ihnen Probleme? –

Antwort

2

Ich ging weiter und wickelte es, und übersetzte dieses Beispiel zu Julia. Dies war nur schnell & schmutzig (es verwendet nicht die Julia Iterator-Schnittstelle, die wäre besser, ich habe es nur die C-API übereinstimmen) Ein paar Punkte: Weil es C zugeordneten Speicher beteiligt ist, richtete ich einen Finalizer Wenn das Iterator-Objekt den Gültigkeitsbereich verlässt und Garbage Collections erfasst, wird der C-Speicher weiterhin freigegeben. Ich habe es auch so gemacht, dass es explizit freigegeben werden kann (und davor geschützt ist, den Speicher zweimal zu befreien). Außerdem haben das Modul und jede Funktion einen Doc-String (den ich auch für den Typ angelegt haben sollte). Es verwendet Ref einen Zeiger auf die Struktur C. passieren

Als ich es lief, habe ich folgendes Ergebnis:

julia> @time example() 
Sum of the primes below 10^10 = 2220820311119164929 
    5.836645 seconds (609 allocations: 16.324 KB) 

Ich hoffe, das ist das richtige Ergebnis!

Hier ist meine schnelle Verpackung von nur Iterator-Schnittstelle:

""" 
    Wrapper for primesieves library 
    Copyright 2016 Scott Paul Jones 
    MIT Licensed 
""" 
module PrimeSieves 

export PrimeSieveIterator, skipto, next, previous, free 

const global psilib = "libprimesieve.dylib" 

type PrimeSieveIterator 
    i::Csize_t 
    last_idx::Csize_t 
    primes::Ptr{UInt64} 
    primes_pimpl::Ptr{UInt64} 
    start::UInt64 
    stop::UInt64 
    stop_hint::UInt64 
    tiny_cache_size::UInt64 
    is_error::Cint 
    function PrimeSieveIterator() 
     piter = new(0,0,C_NULL,C_NULL,0,0,0,0,0) 
     ccall((:primesieve_init, psilib), Void, (Ref{PrimeSieveIterator},), piter) 
     finalizer(piter, free) 
     piter 
    end 
end 

""" Free all memory """ 
function free(piter::PrimeSieveIterator) 
    # Make sure it isn't freed twice 
    if piter.primes != C_NULL 
     ccall((:primesieve_free_iterator, psilib), Void, (Ref{PrimeSieveIterator},), piter) 
     piter.primes = C_NULL 
     piter.primes_pimpl = C_NULL 
    end 
    nothing 
end 
""" Set the primesieve iterator to start """ 
skipto(piter::PrimeSieveIterator, start::Integer, stop_hint::Integer) = 
    ccall((:primesieve_skipto, psilib), Void, 
      (Ref{PrimeSieveIterator}, UInt64, UInt64), piter, start, stop_hint) 

""" Get the next prime """ 
function Base.next(piter::PrimeSieveIterator) 
    (piter.i-1) >= piter.last_idx && 
     ccall((:primesieve_generate_next_primes, psilib), Void, (Ref{PrimeSieveIterator},), piter) 
    unsafe_load(piter.primes, piter.i += 1) 
end 

""" Get the previous prime """ 
function previous(piter::PrimeSieveIterator) 
    if piter.i == 0 
     ccall((:primesieve_generate_previous_primes, psilib), 
       Void, (Ref{PrimeSieveIterator},), piter) 
    else 
     piter.i -= 1 
    end 
    unsafe_load(piter.primes, piter.i + 1) 
end 

end 

Der Beispielcode wäre:

using PrimeSieves 

function example() 
    pit = PrimeSieveIterator() 
    sum = UInt64(0) 
    # iterate over the primes below 10^10 
    while (prime = next(pit)) < UInt64(10000000000) 
     sum += prime 
    end 
    free(pit) 
    println("Sum of the primes below 10^10 = ", sum) 
end 

example() 
+0

Dies ist hervorragend und eine echte Verbesserung der bestehenden Prime-Funktionalität in Julia. Ich frage mich, ob die Lizenz für die C-Bibliothek damit übereinstimmt, dass sie als Julia-Paket verwendet wird? – u003f

+0

Ich bin froh, dass es Ihnen gefällt! Die Quelle ist unter BSD 2-Klausel lizenziert, die fast identisch mit der MIT-Lizenz aussieht. Ich habe das als mentale Übung gemacht, um zu zeigen, dass das Einwickeln von C-Code in Julia nicht schwer ist, ich bin kein Mathematiker. Wenn Sie denken, dass es wirklich nützlich ist, kann ich es als Paket hinzufügen (sollte wahrscheinlich den Rest der API eingehüllt bekommen!) –

+0

Die gute Nachricht: Ihr Code ist 20x schneller als der nächste beste Prime Iterator (was seltsam ist, der einfache Code von increment 'n' und run' isprime (n) '). – u003f