Question

Quel serait l'algorithme le plus optimal (en termes de performances) pour calculer le nombre de diviseurs d'un nombre donné?

Ce serait formidable si vous pouviez fournir un pseudocode ou un lien vers un exemple.

EDIT: Toutes les réponses ont été très utiles, merci. J'utilise le Sieve of Atkin et ensuite, je vais utiliser quelque chose de similaire à ce que Jonathan Leffler a indiqué. Le lien posté par Justin Bozonier contient de plus amples informations sur ce que je voulais.

Était-ce utile?

La solution

Dmitriy a raison de dire que vous voudrez que le tamis d’Atkin génère la liste principale, mais je ne crois pas que cela règle tout le problème. Maintenant que vous avez une liste de nombres premiers, vous aurez besoin de voir combien de ces nombres agissent comme un diviseur (et à quelle fréquence).

Voici quelques exemples de python pour l'algo Regardez ici et recherchez "Sujet". : algorithme mathématique - besoin de diviseurs " ;. Il suffit de compter le nombre d’éléments de la liste au lieu de les renvoyer.

Voici un docteur Math qui explique en quoi consiste exactement ce dont vous avez besoin faire mathématiquement.

Cela revient essentiellement à si votre numéro n est:
  n = a ^ x * b ^ y * c ^ z
(où a, b et c sont les diviseurs premiers de n et x, y et z représentent le nombre de répétitions de ce diviseur) le nombre total de diviseurs est alors:
(x + 1) * (y + 1) * (z + 1) .

Edit: BTW, pour trouver a, b, c, etc., vous aurez envie de faire ce qui équivaut à un algo gourmand si je comprends bien. Commencez avec votre plus grand diviseur premier et multipliez-le par lui-même jusqu'à ce qu'une autre multiplication dépasse le nombre n. Ensuite, passez au facteur le plus bas et multipliez par le nombre précédent ^ nombre de fois où il a été multiplié par le nombre actuel et continuez à le multiplier jusqu'à ce que le prochain dépasse n ... etc. Notez le nombre de fois que vous multipliez le diviseurs ensemble et appliquez ces nombres à la formule ci-dessus.

Je ne suis pas sûr à 100% de la description de mon algo, mais si ce n'est pas le cas, c'est quelque chose de similaire.

Autres conseils

Il existe beaucoup de techniques d’affacturage plus nombreuses que le tamis d’Atkin. Par exemple, supposons que nous voulions factoriser 5893. Eh bien, son carré est de 76,76 ... Nous allons maintenant essayer d'écrire 5893 sous la forme d'un produit de carrés. Bien (77 * 77 - 5893) = 36, ce qui correspond à 6 carrés, donc 5893 = 77 * 77 - 6 * 6 = (77 + 6) (77-6) = 83 * 71. Si cela n’avait pas fonctionné, nous aurions cherché à savoir si 78 * 78 - 5893 était un carré parfait. Etc. Avec cette technique, vous pouvez tester rapidement des facteurs proches de la racine carrée de n beaucoup plus rapidement qu'en testant des nombres premiers individuels. Si vous combinez cette technique pour éliminer les gros nombres premiers avec un tamis, votre méthode de factorisation sera bien meilleure qu’avec le tamis seul.

Et ce n’est que l’une des nombreuses techniques développées. C'est assez simple. Il vous faudrait beaucoup de temps pour apprendre, disons, assez de théorie des nombres pour comprendre les techniques de factorisation basées sur les courbes elliptiques. (Je sais qu'ils existent. Je ne les comprends pas.)

Par conséquent, à moins que vous ne traitiez avec de petits entiers, je ne chercherais pas à résoudre ce problème moi-même. Au lieu de cela, j'essaierais de trouver un moyen d'utiliser quelque chose comme le PARI bibliothèque qui a déjà une solution très efficace mise en œuvre. Avec cela, je peux factoriser un nombre aléatoire de 40 chiffres comme 124321342332143213122323434312213424231341 en environ 0,05 seconde. (Sa factorisation, au cas où vous vous le demanderiez, est 29 * 439 * 1321 * 157907 * 284749 * 33843676813 * 4857795469949. Je suis assez confiant que cela n’a pas été résolu à l’aide du tamis d’Atkin ...)

@Yasky

Votre fonction de diviseur a un bogue dans le fait qu’elle ne fonctionne pas correctement pour les carrés parfaits.

Essayez:

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

Je ne suis pas d’accord sur le fait que le tamis d’Atkin est la meilleure solution, car il pourrait facilement prendre plus de temps pour vérifier chaque nombre dans [1, n] pour la primalité que pour le réduire par divisions.

Voici un code qui, bien que légèrement hackier, est généralement beaucoup plus rapide:

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 Voilà un code python fonctionnel pour résoudre ce problème.

Cette question intéressante est beaucoup plus difficile qu’elle n’apparaît et n’a pas reçu de réponse. La question peut être décomposée en 2 questions très différentes.

1 N donné, trouvez la liste L des facteurs premiers de N

2 étant donné L, calculez le nombre de combinaisons uniques

Toutes les réponses que je vois jusqu’à présent se réfèrent au numéro 1 et omettent de le mentionner, c’est impossible à traiter pour des nombres énormes. Pour les N moyennement dimensionnés, même les nombres 64 bits, c'est facile; pour un énorme N, le problème de l'affacturage peut prendre "pour toujours". Le cryptage à clé publique en dépend.

La question n ° 2 nécessite davantage de discussion. Si L ne contient que des nombres uniques, il s’agit d’un calcul simple utilisant la formule de combinaison pour choisir k objets parmi n éléments. En fait, vous devez additionner les résultats de l’application de la formule en faisant varier k de 1 à sizeof (L). Cependant, L contiendra généralement plusieurs occurrences de plusieurs nombres premiers. Par exemple, L = {2,2,2,3,3,5} est la factorisation de N = 360. Maintenant, ce problème est assez difficile!

Restating n ° 2, étant donné que la collection C contient k éléments, telle que l'élément a a 'des doublons et que l'élément b a b' des doublons, etc. Combien de combinaisons uniques d'éléments 1 à k-1 existe-t-il? Par exemple, {2}, {2,2}, {2,2,2}, {2,3}, {2,2,3,3} doivent apparaître chacun une et une seule fois si L = {2,2 , 2,3,3,5}. Chacune de ces sous-collections est un diviseur unique de N en multipliant les éléments de la sous-collection.

La réponse à votre question dépend grandement de la taille de l’entier. Méthodes pour les petits nombres, par exemple moins de 100 bits, et pour les nombres, environ 1000 bits (comme dans la cryptographie) sont complètement différents.

Voici un algorithme simple O (sqrt (n)). J'ai utilisé cela pour résoudre le projet euler

.
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  

JUSTE une ligne
J'ai réfléchi très attentivement à votre question et j'ai essayé d'écrire un code extrêmement efficace et performant. Pour imprimer tous les diviseurs d'un nombre donné à l'écran, il suffit d'une ligne de code! (utilisez l'option -std = c99 lors de la compilation 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

pour trouver le nombre de diviseurs, vous pouvez utiliser la fonction très très rapide suivante (fonctionne correctement pour tous les nombres entiers sauf 1 et 2)

int number_of_divisors(int n)
{
    int counter,i;
    for(counter=0,i=1;(!(n%i) && (counter++)) || i<=(n/2);i++);
    return counter;
}

ou si vous traitez un nombre donné comme un diviseur (fonctionne correctement pour tous les nombres entiers sauf 1 et 2)

int number_of_divisors(int n)
{
    int counter,i;
    for(counter=0,i=1;(!(n%i) && (counter++)) || i<=(n/2);i++);
    return ++counter;
}

REMARQUE: les deux fonctions ci-dessus fonctionnent correctement pour tous les nombres entiers positifs, sauf les nombres 1 et 2. il est donc fonctionnel pour tous les nombres supérieurs à 2 mais si vous avez besoin de couvrir 1 et 2, vous pouvez utiliser l’une des fonctions suivantes (un peu plus lentement)

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

OU

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

small is beautiful:)

Le tamis d’Atkin est une version optimisée du tamis d’Eratosthenes qui donne tous les nombres premiers jusqu’à un entier donné. Vous devriez être capable de google ceci pour plus de détails.

Une fois que vous avez cette liste, il est simple de diviser votre nombre par chaque nombre premier pour voir s'il s'agit d'un diviseur exact (le reste est égal à zéro).

Les étapes de base pour calculer les diviseurs pour un nombre (n) sont [Ceci est un pseudocode converti à partir de code réel, donc j'espère que je n'ai pas introduit d'erreurs]:

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

Vous pouvez essayer celui-ci. C'est un peu féroce, mais assez rapide.

def factors(n):
    for x in xrange(2,n):
        if n%x == 0:
            return (x,) + factors(n/x)
    return (n,1)

Une fois la factorisation principale, il existe un moyen de trouver le nombre de diviseurs. Ajoutez un à chacun des exposants de chaque facteur, puis multipliez les exposants ensemble.

Par exemple: 36 Factorisation initiale: 2 ^ 2 * 3 ^ 2 Diviseurs: 1, 2, 3, 4, 6, 9, 12, 18, 36 Nombre de diviseurs: 9

Ajoutez un à chaque exposant 2 ^ 3 * 3 ^ 3 Exposants multipliés: 3 * 3 = 9

Avant de vous engager dans une solution, considérez que l’approche Sieve peut ne pas être une bonne réponse dans le cas typique.

Il y a quelque temps, il y avait une question primordiale et j'ai fait un test de temps - pour les entiers 32 bits, au moins déterminer si le nombre premier était plus lent que la force brute. Il y a deux facteurs:

1) Tandis qu'un humain met un certain temps à faire une division, il est très rapide sur l'ordinateur - un coût similaire à celui de la recherche de la réponse.

2) Si vous n'avez pas de table principale, vous pouvez créer une boucle entièrement exécutée dans le cache L1. Cela le rend plus rapide.

C’est une solution efficace:

#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;
}

Les diviseurs font quelque chose de spectaculaire: ils se divisent complètement. Si vous souhaitez vérifier le nombre de diviseurs pour un nombre, n , il est clairement redondant de couvrir tout le spectre, 1 ... n . Je n'ai pas fait de recherche approfondie à ce sujet, mais j'ai résolu le problème 12 du projet Euler sur les numéros triangulaires. . Ma solution pour le test supérieur à 500 diviseurs a fonctionné pendant 309504 microsecondes (~ 0,3 s). J'ai écrit cette fonction de diviseur pour la solution.

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

À chaque algorithme, il y a un point faible. Je pensais que c'était faible contre les nombres premiers. Mais puisque les nombres triangulaires ne sont pas imprimés, il a parfaitement rempli sa fonction. D'après mon profilage, je pense que cela s'est plutôt bien passé.

Joyeuses fêtes.

Vous voulez le tamis d'Atkin, décrit ici: http://en.wikipedia.org/wiki / Sieve_of_Atkin

la méthode des nombres premiers est très claire ici. P [] est une liste de nombres premiers inférieurs ou égaux à sq = sqrt (n);

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  .

Les manuels de théorie des nombres appellent la fonction de comptage des diviseurs tau. Le premier fait intéressant est que c'est multiplicatif, c'est à dire. t (ab) = t (a) t (b), quand a et b n'ont pas de facteur commun. (Preuve: chaque paire de diviseurs de a et b donne un diviseur distinct de ab).

Notons maintenant que pour p un nombre premier, t (p ** k) = k + 1 (les puissances de p). Ainsi, vous pouvez facilement calculer t (n) à partir de sa factorisation.

Cependant, la factorisation de grands nombres peut être lente (la sécurité de la cryptographie RSA dépend du fait que deux grands nombres premiers sont difficiles à factoriser). Cela suggère cet algorithme optimisé

  1. Tester si le nombre est premier (rapide)
  2. Si tel est le cas, renvoyez 2
  3. Sinon, factoriser le nombre (lent si plusieurs facteurs premiers sont importants)
  4. Calculez t (n) à partir de la factorisation

Ce qui suit est un programme C pour trouver le nombre de diviseurs d’un nombre donné.

La complexité de l'algorithme ci-dessus est O (sqrt (n)).

Cet algorithme fonctionnera correctement pour les nombres qui sont des carrés parfaits ainsi que pour les nombres qui ne sont pas des carrés parfaits.

Notez que la limite supérieure de la boucle est définie sur la racine carrée du nombre pour que l'algorithme soit le plus efficace.

Notez que le stockage de la limite supérieure dans une variable distincte permet également de gagner du temps. Vous ne devez pas appeler la fonction sqrt dans la section condition de la boucle for, cela permet également de gagner du temps de calcul.

#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;
}

Au lieu de la boucle for ci-dessus, vous pouvez également utiliser la boucle suivante, qui est encore plus efficace, car elle supprime la nécessité de rechercher la racine carrée du nombre.

for(i=2;i*i<=n;i++)
{
    ...
}

Voici une fonction que j'ai écrite. la pire complexité temporelle est O (sqrt (n)), le meilleur temps par contre est O (log (n)). Il vous donne tous les premiers diviseurs ainsi que le nombre de ses occurrences.

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

C’est le moyen le plus simple de calculer les diviseurs de nombres:

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

    }
}

@Kendall

J'ai testé votre code et apporté des améliorations. Il est maintenant encore plus rapide. J'ai également testé avec le code @ ???? ????????, ce qui est également plus rapide que son 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;
}

N’est-ce pas simplement une question de factorisation du nombre - de détermination de tous les facteurs du nombre? Vous pouvez ensuite décider si vous avez besoin de toutes les combinaisons d’un ou de plusieurs facteurs.

Donc, un algorithme possible serait:

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

Il vous appartient ensuite de combiner les facteurs pour déterminer le reste de la réponse.

C’est quelque chose que j’ai imaginé à partir de la réponse de Justin. Cela pourrait nécessiter une optimisation.

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

Je pense que c’est ce que vous recherchez. Je fais exactement ce que vous avez demandé. Copiez-le et collez-le dans Notepad.Enregistrez-le sous le numéro * .bat.Run.Enter.Multipliez le processus par 2 et indiquez le nombre de diviseurs.J'ai fait exprès de le déterminer plus rapidement:

Veuillez noter qu'une variable CMD peut prendre en charge des valeurs supérieures à 999999999

.
@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

Je suppose que celui-ci sera pratique et précis

script.pyton

> > > facteurs = [x pour x dans l'intervalle (1, n + 1) si n% x == 0] print len ??(facteurs)

Essayez quelque chose dans ce sens:

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

Vous pouvez précalculer les nombres premiers jusqu'à la racine carrée du nombre maximal de N possible et calculer l'exposant de chaque facteur premier d'un nombre. Le nombre de diviseurs de n (n = p1 ^ a p2 ^ b p3 ^ c ...) est (a + 1) (b + 1) (c + 1) car il est identique à compter le mode de combinaison des nombres premiers nombre de ces facteurs (et cela comptera le nombre de diviseurs). Ceci est très rapide si vous pré-calculez les nombres premiers

Informations plus détaillées sur cette méthode:

https://mathschallenge.net/library/number/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;
    }
}

Je ne connais pas la méthode la PLUS efficace, mais j'aimerais procéder comme suit:

  • Créez une table de nombres premiers pour trouver tous les nombres premiers inférieurs ou égaux à la racine carrée du nombre (Personnellement, j'utiliserais le tamis d'Atkin)
  • Comptez tous les nombres premiers inférieurs ou égaux à la racine carrée du nombre et multipliez-le par deux. Si la racine carrée du nombre est un entier, soustrayez-en un de la variable count.

Devrait fonctionner \ o /

Si vous avez besoin, je peux coder quelque chose demain en C pour le démontrer.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top