Was wäre der beste Algorithmus (leistungsmäßig), um die Anzahl der Divisoren einer bestimmten Anzahl zu berechnen?
Es wäre großartig, wenn Sie einen Pseudocode oder einen Link zu einem Beispiel angeben könnten.
EDIT: Alle Antworten waren sehr hilfreich, danke. Ich implementiere das Sieb von Atkin und werde dann etwas ähnliches verwenden, was Jonathan Leffler angedeutet hat. Der Link von Justin Bozonier enthält weitere Informationen darüber, was ich wollte.
Dmitriy hat recht, dass Sie möchten, dass das Sieb von Atkin die Primeliste generiert, aber ich glaube nicht, dass sich das um alles kümmert. Nun, da Sie eine Liste von Primzahlen haben, müssen Sie sehen, wie viele dieser Primen als Divisor wirken (und wie oft).
Hier ist ein bisschen Python für das Algo Schau hier und suche nach "Subject: math - need divisors algorithm". Zählen Sie einfach die Anzahl der Elemente in der Liste, anstatt sie zurückzugeben.
Hier ist ein Dr. Math das erklärt, was genau du mathematisch tun musst.
Im Wesentlichen läuft es darauf hinaus, wenn Ihre Nummer n
ist:n = a^x * b^y * c^z
(wobei a, b und c die Hauptunterteiler von n sind und x, y und z die Anzahl der Wiederholungen des Teilers sind) Dann ist die Gesamtzahl für alle Teiler:(x + 1) * (y + 1) * (z + 1)
.
Bearbeiten: Übrigens, um a, b, c usw. zu finden, sollten Sie tun, was einem gierigen Algo gleichkommt, wenn ich das richtig verstehe. Beginnen Sie mit Ihrem größten Hauptteiler und multiplizieren Sie ihn mit sich selbst, bis eine weitere Multiplikation die Zahl n überschreitet. Gehen Sie dann zum nächstniedrigsten Faktor über und multiplizieren Sie die Anzahl der Male, die er mit der aktuellen Primzahl multipliziert hat, und multiplizieren Sie die Primzahl so lange, bis die nächste Zahl n überschreitet Teiler zusammen und wendet diese Zahlen in der obigen Formel an.
Ich bin nicht 100% sicher über meine Beschreibung von Algo, aber wenn das nicht so ist, ist es etwas Ähnliches.
Es gibt ein lot mehr Techniken zum Faktorisieren als das Sieb von Atkin. Nehmen wir zum Beispiel an, wir wollen 5893 einkalkulieren. Nun, sein Quadrat ist 76,76 ... Jetzt werden wir versuchen, 5893 als ein Produkt von Quadraten zu schreiben. Well (77 * 77 - 5893) = 36, das 6 Quadrat ist, also 5893 = 77 * 77 - 6 * 6 = (77 + 6) (77-6) = 83 * 71. Wenn das nicht funktioniert hätte, hätten wir geprüft, ob 78 * 78 - 5893 ein perfektes Quadrat ist. Und so weiter. Mit dieser Technik können Sie schnell auf Faktoren in der Nähe der Quadratwurzel von n testen, als mit einzelnen Primzahlen. Wenn Sie diese Technik kombinieren, um große Primzahlen mit einem Sieb auszuschließen, haben Sie eine weitaus bessere Faktorisierungsmethode als mit dem Sieb allein.
Und dies ist nur eine von vielen Techniken, die entwickelt wurden. Dies ist ziemlich einfach. Es würde lange dauern, bis Sie genügend Theorie gelernt haben, um die auf elliptischen Kurven basierenden Faktorisierungstechniken zu verstehen. (Ich weiß, dass es sie gibt. Ich verstehe sie nicht.)
Wenn Sie sich also nicht mit kleinen Zahlen beschäftigen, würde ich nicht versuchen, dieses Problem selbst zu lösen. Stattdessen würde ich versuchen, eine Möglichkeit zu finden, etwas wie die Bibliothek PARI zu verwenden, in der bereits eine hocheffiziente Lösung implementiert ist. Damit kann ich eine zufällige 40-stellige Zahl wie etwa 124321342332143213122323434312213424231341 in etwa 0,05 Sekunden einrechnen. (Falls Sie sich gefragt haben, ist die Faktorisierung 29 * 439 * 1321 * 157907 * 284749 * 33843676813 * 4857795469949. Ich bin ziemlich zuversichtlich, dass dies mit dem Sieb von Atkin nicht gelang ...)
@ Yasky
Ihre Divisor-Funktion hat den Fehler, dass sie für perfekte Quadrate nicht korrekt funktioniert.
Versuchen:
int divisors(int x) {
int limit = x;
int numberOfDivisors = 0;
if (x == 1) return 1;
for (int i = 1; i < limit; ++i) {
if (x % i == 0) {
limit = x / i;
if (limit != i) {
numberOfDivisors++;
}
numberOfDivisors++;
}
}
return numberOfDivisors;
}
Ich bin nicht der Meinung, dass das Sieb von Atkin der richtige Weg ist, denn es könnte leicht länger dauern, um jede Zahl in [1, n] auf Primalität zu prüfen, als die Zahl durch Divisionen zu reduzieren.
Hier ist ein Code, der zwar etwas hackiger ist, im Allgemeinen jedoch viel schneller ist:
import operator
# A slightly efficient superset of primes.
def PrimesPlus():
yield 2
yield 3
i = 5
while True:
yield i
if i % 6 == 1:
i += 2
i += 2
# Returns a dict d with n = product p ^ d[p]
def GetPrimeDecomp(n):
d = {}
primes = PrimesPlus()
for p in primes:
while n % p == 0:
n /= p
d[p] = d.setdefault(p, 0) + 1
if n == 1:
return d
def NumberOfDivisors(n):
d = GetPrimeDecomp(n)
powers_plus = map(lambda x: x+1, d.values())
return reduce(operator.mul, powers_plus, 1)
ps Das ist Python-Code, um dieses Problem zu lösen.
Diese interessante Frage ist viel schwieriger als sie aussieht und wurde nicht beantwortet. Die Frage kann in zwei sehr unterschiedliche Fragen eingeteilt werden.
Alle Antworten, die ich bis jetzt sehe, beziehen sich auf # 1 und können nicht erwähnt werden. Für mittelgroße N, sogar 64-Bit-Zahlen, ist dies einfach; für enormes N kann das Faktorisierungsproblem "für immer" dauern. Die Verschlüsselung mit öffentlichen Schlüsseln hängt davon ab.
Frage 2 braucht mehr Diskussion. Wenn L nur eindeutige Nummern enthält, handelt es sich um eine einfache Berechnung mit der Kombinationsformel zur Auswahl von k Objekten aus n Elementen. Tatsächlich müssen Sie die Ergebnisse aus der Anwendung der Formel summieren und dabei k von 1 bis sizeof (L) variieren. L enthält jedoch normalerweise mehrere Vorkommen mehrerer Primzahlen. Zum Beispiel ist L = {2,2,2,3,3,5} die Faktorisierung von N = 360. Jetzt ist dieses Problem ziemlich schwierig!
Nochmal 2, wenn die Sammlung C k Elemente enthält, so dass Element a Duplikate hat und Element B b Duplikate hat. Beispielsweise müssen {2}, {2,2}, {2,2,2}, {2,3}, {2,2,3,3} jeweils einmal und nur einmal vorkommen, wenn L = {2,2 2,3,3,5}. Jede dieser einzigartigen Untersammlungen ist ein einzigartiger Teiler von N, indem die Elemente in der Untersammlung multipliziert werden.
Eine Antwort auf Ihre Frage hängt stark von der Größe der Ganzzahl ab. Verfahren für kleine Anzahlen, z. weniger als 100 Bit und für Zahlen ~ 1000 Bit (wie sie in der Kryptographie verwendet werden) sind völlig unterschiedlich.
allgemeiner Überblick: http://en.wikipedia.org/wiki/Divisor_function
werte für kleine n
und einige nützliche Referenzen: AOOOOO5: d(n) (auch tau (n) oder sigma_0 (n)), die Anzahl der Divisoren von n.
praxisbeispiel: Faktorisierung ganzer Zahlen
Hier ist ein einfacher O(sqrt(n)) Algorithmus. Ich habe das verwendet, um Projekt Euler zu lösen
def divisors(n):
count=2 # accounts for 'n' and '1'
i=2
while(i**2 < n):
if(n%i==0):
count+=2
i+=1
count+=(1 if i**2==n else 0)
return count
JUST eine Zeile
Ich habe sehr sorgfältig über Ihre Frage nachgedacht und versucht, einen sehr effizienten und performanten Code zu schreiben. Um alle Divisoren einer bestimmten Zahl auf dem Bildschirm zu drucken, benötigen wir nur eine Codezeile! ( benutze die Option -std = c99 während des Kompilierens via gcc)
for(int i=1,n=9;((!(n%i)) && printf("%d is a divisor of %d\n",i,n)) || i<=(n/2);i++);//n is your number
zum Auffinden von Divisorenzahlen können Sie die folgende sehr schnelle Funktion verwenden (funktioniert für alle Integer-Zahlen außer 1 und 2)
int number_of_divisors(int n)
{
int counter,i;
for(counter=0,i=1;(!(n%i) && (counter++)) || i<=(n/2);i++);
return counter;
}
oder wenn Sie eine gegebene Zahl als Divisor behandeln (funktionieren Sie für alle ganzen Zahlen außer 1 und 2)
int number_of_divisors(int n)
{
int counter,i;
for(counter=0,i=1;(!(n%i) && (counter++)) || i<=(n/2);i++);
return ++counter;
}
ANMERKUNG: Zwei der oben genannten Funktionen funktionieren für alle positiven Integer-Nummern außer Nummer 1 und 2 Es funktioniert also für alle Zahlen, die größer als 2 sind. Wenn Sie jedoch 1 und 2 abdecken müssen, können Sie eine der beiden verwenden die folgenden Funktionen (etwas langsamer)
int number_of_divisors(int n)
{
int counter,i;
for(counter=0,i=1;(!(n%i) && (counter++)) || i<=(n/2);i++);
if (n==2 || n==1)
{
return counter;
}
return ++counter;
}
OR
int number_of_divisors(int n)
{
int counter,i;
for(counter=0,i=1;(!(i==n) && !(n%i) && (counter++)) || i<=(n/2);i++);
return ++counter;
}
klein ist schön :)
Sie könnten dies versuchen. Es ist ein bisschen hackig, aber es ist ziemlich schnell.
def factors(n):
for x in xrange(2,n):
if n%x == 0:
return (x,) + factors(n/x)
return (n,1)
Das Sieb von Atkin ist eine optimierte Version des Siebs von Eratosthenes, die alle Primzahlen bis zu einer bestimmten Zahl angibt. Sie sollten in der Lage sein, dies für mehr Details zu googeln.
Sobald Sie diese Liste haben, ist es einfach, Ihre Zahl durch jeden Strich zu teilen, um zu sehen, ob es sich um einen exakten Divisor handelt (d. H. Der Rest ist Null).
Die grundlegenden Schritte zum Berechnen der Divisoren für eine Zahl (n) lauten: [Dies ist ein Pseudocode, der aus echtem Code konvertiert wurde, daher hoffe ich, dass ich keine Fehler eingeführt habe]:
for z in 1..n:
prime[z] = false
prime[2] = true;
prime[3] = true;
for x in 1..sqrt(n):
xx = x * x
for y in 1..sqrt(n):
yy = y * y
z = 4*xx+yy
if (z <= n) and ((z mod 12 == 1) or (z mod 12 == 5)):
prime[z] = not prime[z]
z = z-xx
if (z <= n) and (z mod 12 == 7):
prime[z] = not prime[z]
z = z-yy-yy
if (z <= n) and (x > y) and (z mod 12 == 11):
prime[z] = not prime[z]
for z in 5..sqrt(n):
if prime[z]:
zz = z*z
x = zz
while x <= limit:
prime[x] = false
x = x + zz
for z in 2,3,5..n:
if prime[z]:
if n modulo z == 0 then print z
Sobald Sie die Primfaktorisierung abgeschlossen haben, können Sie die Anzahl der Teiler ermitteln. Addiere zu jedem Exponenten zu jedem einzelnen Faktor einen und multipliziere die Exponenten zusammen.
Zum Beispiel: 36Prime Faktorisierung: 2 ^ 2 * 3 ^ 2 Divisoren: 1, 2, 3, 4, 6, 9, 12, 18, 36 .__ Anzahl der Divisoren: 9
Addiere zu jedem Exponenten 2 ^ 3 * 3 ^ 3 Multipliziere Exponenten: 3 * 3 = 9
Bevor Sie sich zu einer Lösung bekennen, sollten Sie bedenken, dass der Sieve-Ansatz im typischen Fall keine gute Antwort ist.
Vor einiger Zeit gab es eine Hauptfrage und ich machte einen Zeittest - für 32-Bit-Ganzzahlen, die zumindest feststellten, ob die Primzahl langsamer war als die rohe Gewalt. Es gibt zwei Faktoren, die vor sich gehen:
1) Während ein Mensch eine Zeit braucht, um eine Division durchzuführen, ist er sehr schnell am Computer - ähnlich wie bei der Suche nach Antworten.
2) Wenn Sie keine Prim-Tabelle haben, können Sie eine Schleife erstellen, die vollständig im L1-Cache ausgeführt wird. Das macht es schneller.
Dies ist eine effiziente Lösung:
#include <iostream>
int main() {
int num = 20;
int numberOfDivisors = 1;
for (int i = 2; i <= num; i++)
{
int exponent = 0;
while (num % i == 0) {
exponent++;
num /= i;
}
numberOfDivisors *= (exponent+1);
}
std::cout << numberOfDivisors << std::endl;
return 0;
}
Divisoren machen etwas Großartiges: Sie teilen sich vollständig. Wenn Sie die Anzahl der Divisoren für eine Zahl n
überprüfen möchten, ist es eindeutig überflüssig, das gesamte Spektrum 1...n
abzudecken. Ich habe hierzu keine eingehenden Nachforschungen angestellt, aber ich habe Projekt Eulers Problem 12 über Dreieckszahlen gelöst. Meine Lösung für den Test größer als 500 Divisoren lief 309504 Mikrosekunden (~ 0,3 Sekunden). Ich habe diese Divisor-Funktion für die Lösung geschrieben.
int divisors (int x) {
int limit = x;
int numberOfDivisors = 1;
for (int i(0); i < limit; ++i) {
if (x % i == 0) {
limit = x / i;
numberOfDivisors++;
}
}
return numberOfDivisors * 2;
}
Zu jedem Algorithmus gibt es einen Schwachpunkt. Ich dachte, dass dies gegen die Primzahlen schwach sei. Da aber dreieckige Zahlen nicht gedruckt werden, hat sie ihren Zweck einwandfrei erfüllt. Aus meiner Profilierung heraus hat es meiner Meinung nach gut funktioniert.
Schöne Ferien.
Das Sieb von Atkin soll hier beschrieben werden: http://en.wikipedia.org/wiki/Sieve_of_Atkin
@ Kendall
Ich habe Ihren Code getestet und einige Verbesserungen vorgenommen, jetzt ist er sogar noch schneller ..__ Ich habe auch mit @ هومن جاویدپور Code getestet, dieser ist auch schneller als sein Code.
long long int FindDivisors(long long int n) {
long long int count = 0;
long long int i, m = (long long int)sqrt(n);
for(i = 1;i <= m;i++) {
if(n % i == 0)
count += 2;
}
if(n / m == m && n % m == 0)
count--;
return count;
}
Hier ist eine Funktion, die ich geschrieben habe. Die schlechteste Zeitkomplexität ist O (sqrt (n)), die beste Zeit dagegen ist O (log (n)). Es gibt Ihnen alle Hauptteiler zusammen mit der Anzahl ihres Auftretens.
public static List<Integer> divisors(n) {
ArrayList<Integer> aList = new ArrayList();
int top_count = (int) Math.round(Math.sqrt(n));
int new_n = n;
for (int i = 2; i <= top_count; i++) {
if (new_n == (new_n / i) * i) {
aList.add(i);
new_n = new_n / i;
top_count = (int) Math.round(Math.sqrt(new_n));
i = 1;
}
}
aList.add(new_n);
return aList;
}
Das folgende ist ein C-Programm zum Ermitteln der Anzahl der Teiler einer gegebenen Anzahl.
Die Komplexität des obigen Algorithmus ist O (sqrt (n)).
Dieser Algorithmus funktioniert korrekt für die Zahl, die perfektes Quadrat ist, sowie für Zahlen, die nicht perfektes Quadrat sind.
Beachten Sie, dass die obere Grenze der Schleife auf die Quadratwurzel von number gesetzt ist, damit der Algorithmus am effizientesten ist.
Das Speichern der Obergrenze in einer separaten Variablen spart auch Zeit. Sie sollten die sqrt-Funktion nicht im Bedingungsabschnitt der for-Schleife aufrufen. Dies spart auch Ihre Rechenzeit.
#include<stdio.h>
#include<math.h>
int main()
{
int i,n,limit,numberOfDivisors=1;
printf("Enter the number : ");
scanf("%d",&n);
limit=(int)sqrt((double)n);
for(i=2;i<=limit;i++)
if(n%i==0)
{
if(i!=n/i)
numberOfDivisors+=2;
else
numberOfDivisors++;
}
printf("%d\n",numberOfDivisors);
return 0;
}
Anstelle der obigen for-Schleife können Sie auch die folgende Schleife verwenden, die noch effizienter ist, da dadurch die Quadratwurzel der Zahl nicht gefunden werden muss.
for(i=2;i*i<=n;i++)
{
...
}
Dies ist der grundlegendste Weg, um die Anzahl der Divissoren zu berechnen:
class PrintDivisors
{
public static void main(String args[])
{
System.out.println("Enter the number");
// Create Scanner object for taking input
Scanner s=new Scanner(System.in);
// Read an int
int n=s.nextInt();
// Loop from 1 to 'n'
for(int i=1;i<=n;i++)
{
// If remainder is 0 when 'n' is divided by 'i',
if(n%i==0)
{
System.out.print(i+", ");
}
}
// Print [not necessary]
System.out.print("are divisors of "+n);
}
}
Zahlentheorie-Lehrbücher nennen die Divisor-Counting-Funktion Tau. Die erste interessante Tatsache ist, dass es multiplikativ ist, dh. τ (ab) = τ (a) τ (b), wenn a und b keinen gemeinsamen Faktor haben. (Beweis: Jedes Paar von Teilern von a und b ergibt einen eindeutigen Teiler von ab).
Man beachte nun, dass für p eine Primzahl τ (p ** k) = k + 1 (die Potenzen von p) ist. Somit können Sie τ (n) leicht aus seiner Faktorisierung berechnen.
Die Faktorisierung großer Zahlen kann jedoch langsam sein (die Sicherheit der RSA-Crytopraphie hängt davon ab, ob zwei große Primzahlen schwer zu faktorisieren sind). Das deutet auf diesen optimierten Algorithmus hin
die Primzahlmethode ist hier sehr klar. P [] ist eine Liste von Primzahlen, die kleiner oder gleich dem sq = sqrt (n) sind;
for (int i = 0 ; i < size && P[i]<=sq ; i++){
nd = 1;
while(n%P[i]==0){
n/=P[i];
nd++;
}
count*=nd;
if (n==1)break;
}
if (n!=1)count*=2;//the confusing line :D :P .
i will lift the understanding for the reader .
i now look forward to a method more optimized .
ich denke, dieses wird sowohl praktisch als auch präzise sein
>>>factors=[ x for x in range (1,n+1) if n%x==0]
print len(factors)
Ist es nicht nur eine Frage der Faktorisierung der Zahl, die alle Faktoren der Zahl bestimmt? Sie können dann entscheiden, ob Sie alle Kombinationen eines oder mehrerer Faktoren benötigen.
Ein möglicher Algorithmus wäre also:
factor(N)
divisor = first_prime
list_of_factors = { 1 }
while (N > 1)
while (N % divisor == 0)
add divisor to list_of_factors
N /= divisor
divisor = next_prime
return list_of_factors
Es liegt an Ihnen, die Faktoren zu kombinieren, um den Rest der Antwort zu bestimmen.
Dies ist etwas, das ich basierend auf Justin Antwort hatte. Möglicherweise bedarf es einer Optimierung.
n=int(input())
a=[]
b=[]
def sieve(n):
np = n + 1
s = list(range(np))
s[1] = 0
sqrtn = int(n**0.5)
for i in range(2, sqrtn + 1):
if s[i]:
s[i*i: np: i] = [0] * len(range(i*i, np, i))
return filter(None, s)
k=list(sieve(n))
for i in range(len(k)):
if n%k[i]==0:
a.append(k[i])
a.sort()
for i in range(len(a)):
j=1
while n%(a[i]**j)==0:
j=j+1
b.append(j-1)
nod=1
for i in range(len(b)):
nod=nod*(b[i]+1)
print('no.of divisors of {} = {}'.format(n,nod))
Sie können Primzahlen bis zur Quadratwurzel des maximal möglichen N vorberechnen und den Exponenten jedes Primfaktors einer Zahl berechnen. Die Anzahl der Divisoren von n (n = p1 ^ ap2 ^ bp3 ^ c ...) beträgt (a + 1) (b + 1) (c + 1), da dies derselbe ist, wie die Primzahl zu zählen Anzahl dieser Faktoren (und dies zählt die Anzahl der Divisoren) .. Dies ist sehr schnell, wenn Sie die Primzahlen vorberechnen
Weitere Informationen zu dieser Methode:
https://mathschallenge.net/library/number/number_of_divisors
https://www.math.upenn.edu/~deturck/m170/wk2/numdivisors.html
http://primes.utm.edu/glossary/xpage/tau.html
#include <iostream>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
int divisors_count(const vector<int>& primes, int n)
{
int divisors = 1;
for (int i = 0; i < primes.size(); ++i) {
int factor = primes[i];
int factor_exponent = 0;
while (n % factor == 0) {
++factor_exponent;
n /= factor;
}
divisors *= (factor_exponent + 1);
}
if (n > 1)
return 2*divisors; // prime factor > sqrt(MAX_N)
return divisors;
}
int main()
{
const int MAX_N = 1e6;
int max_factor = sqrt(MAX_N);
vector<char> prime(max_factor + 1, true);
for (int i = 3; i <= max_factor; i += 2) {
if (prime[i]) {
for (int j = 3*i; j <= max_factor; j += 2*i) {
prime[j] = false;
}
}
}
vector<int> primes;
primes.reserve(max_factor/2);
primes.Push_back(2);
for (int i = 3; i <= max_factor; i += 2) {
if (prime[i]) {
primes.Push_back(i);
}
}
int n;
while (cin >> n) {
cout << divisors_count(primes, n) << endl;
}
}
Versuchen Sie etwas in dieser Richtung:
int divisors(int myNum) {
int limit = myNum;
int divisorCount = 0;
if (x == 1)
return 1;
for (int i = 1; i < limit; ++i) {
if (myNum % i == 0) {
limit = myNum / i;
if (limit != i)
divisorCount++;
divisorCount++;
}
}
return divisorCount;
}
Ich denke, das ist, wonach Sie suchen. Ich erledige genau das, wonach Sie gefragt haben. Kopieren Sie es und fügen Sie es in den Editor ein Ich habe das absichtlich so gemacht, dass es die Divisoren schneller bestimmt:
Bitte beachten Sie, dass eine differenzierbare CMD-Werte Werte über 999999999 nicht unterstützen können
@echo off
modecon:cols=100 lines=100
:start
title Enter the Number to Determine
cls
echo Determine a number as a product of 2 numbers
echo.
echo Ex1 : C = A * B
echo Ex2 : 8 = 4 * 2
echo.
echo Max Number length is 9
echo.
echo If there is only 1 proces done it
echo means the number is a prime number
echo.
echo Prime numbers take time to determine
echo Number not prime are determined fast
echo.
set /p number=Enter Number :
if %number% GTR 999999999 goto start
echo.
set proces=0
set mindet=0
set procent=0
set B=%Number%
:Determining
set /a mindet=%mindet%+1
if %mindet% GTR %B% goto Results
set /a solution=%number% %%% %mindet%
if %solution% NEQ 0 goto Determining
if %solution% EQU 0 set /a proces=%proces%+1
set /a B=%number% / %mindet%
set /a procent=%mindet%*100/%B%
if %procent% EQU 100 set procent=%procent:~0,3%
if %procent% LSS 100 set procent=%procent:~0,2%
if %procent% LSS 10 set procent=%procent:~0,1%
title Progress : %procent% %%%
if %solution% EQU 0 echo %proces%. %mindet% * %B% = %number%
goto Determining
:Results
title %proces% Results Found
echo.
@pause
goto start