33 votes

Meilleure solution pour trouver des nombres ayant exactement 3 diviseurs

J'étudiais la programmation et j'ai trouvé un exercice consistant à écrire un algorithme permettant de trouver les "nombres à trois" (nombres qui sont divisibles par exactement 3 nombres). J'ai écrit ceci :

function threesomeNumber(N) {
    var found = 0;
    var i = 1;
    var numberOfDivisions = 1;
    while (found < N) {
        for (var j = 2; j <= i; j++) {
            if (i % j === 0) {
                numberOfDivisions++;
            }
        }
        if (numberOfDivisions === 3) {
            found++;
            console.log(found + " = " + i);
        }
        numberOfDivisions = 1;
        i++;
    }
}

Le problème est qu'il fonctionne assez lentement et je me demandais s'il pouvait être fait plus rapidement. Quelqu'un connaît-il une solution plus optimisée ? Je veux qu'il trouve N nombres consécutifs à trois.

8 votes

Cela fait un moment que je cherche une meilleure solution pour trouver des trios. Mon approche habituelle n'a fonctionné qu'une seule fois, et au milieu de celle-ci, j'ai rencontré une erreur d'exécution.

0 votes

Ces noms de variables ne sont pas sérieux, je vais les éditer pour plus de clarté.

17 votes

@Krzysztof, je pense que cette question va recueillir beaucoup de votes rien que par son titre. Indice : en anglais, plan à trois ne signifie pas réellement les nombres qui sont divisibles par exactement 3 nombres .

46voto

Hermann Döppes Points 758

Les seuls nombres à trois sont des carrés de nombres premiers (diviseurs 1, p, p^2). Il suffit de faire Erathostenes et de retourner les carrés.

Preuve : S'il a un nombre impair de diviseurs, on sait que c'est un carré. Puisque 1 et n^2 sont toujours des diviseurs de n^2, nous ne pouvons avoir qu'un diviseur de plus, c'est-à-dire n. Tout diviseur de n serait un autre diviseur de n^2, donc n est premier.

Exemple (basé sur le code donné) :

function threesomeNumber(N) {
var found = 0;
var i = 2;
var prime = true;
while (found < N) {
    // Naive prime test, highly inefficient
    for (var j = 2; j*j <= i; j++) {
        if (i % j === 0) {
            prime = false;
        }
    }
    if (prime) {
        found++;
        console.log(found + " = " + (i*i));
    }
    prime = true;
    i++;
  }
}

0 votes

Pourquoi pas les produits de deux nombres premiers ?

2 votes

@PatrickCollins Alors il aura quatre diviseurs au lieu de trois : 1, deux nombres premiers, et le produit lui-même.

0 votes

@KenHung Ah, je ne pensais pas au produit lui-même. Merci !

15voto

Andrea Dusza Points 1550

Vous pourriez implémenter un algorithme basé sur le tamis d'Eratosthène . Le seul changement est que vous ne marquez pas les multiples des nombres premiers trouvés, mais les multiples des nombres trouvés qui ont au moins 3 diviseurs. La raison en est que vous pouvez être sûr que les multiples de ces nombres ont plus de 3 diviseurs.

EDIT : La solution de Hermann est la meilleure pour les "trios". Mon idée est plus générale et applicable pour les "N-somes".

4voto

MIE Points 204

Une solution plus optimisée consiste à trouver la première N Les nombres premiers et les carrés. L'idée derrière cela est que les nombres premiers sont divisibles par seulement deux nombres. Donc les nombres divisibles par seulement trois nombres ont un diviseur supplémentaire qui doit être leur racine carrée. Il doit s'agir d'un nombre premier pour ne pas ajouter de diviseur supplémentaire aux diviseurs du nombre principal.

function threesomeNumber(N){
    return firstPrimes(N).map(function(x){return x*x})
}

firstPrimes est une fonction qui renvoie le premier N primes.

0 votes

Ce n'est pas le cas :) . Je le terminais quand j'ai vu sa réponse. Je ne pouvais pas le jeter comme ça.

2voto

Boom Points 196

En voici une simple :

function threesomeNumber(N)
{
    var found = 0;
    var i = 1;
    var numberOfDivisions = 1;

    while(found < N)
    {
        for(var j = 2; j <= i; j++)
        {
            if(i % j === 0)
                numberOfDivisions++;

            // Stop trying if more that 3 Divisions are Found
            if(numberOfDivisions > 3)
               break;
        }

        if(numberOfDivisions === 3)
        {
            found++;
            console.log(found + " = " + i);
        }

        numberOfDivisions = 1;
        i++;
    }
}

1 votes

Ce n'est pas une bonne réponse, je demandais une version plus optimisée et vous avez donné la même solution que moi en premier lieu. Voir la réponse acceptée.

1 votes

J'ai ajouté une pause, ce qui a accéléré un peu les choses.

2 votes

@KrzysztofKraszewski cette réponse offre une légère optimisation par rapport à votre tentative initiale : la rupture conditionnelle.

1voto

En voici une dont la complexité temporelle est de O(N^(1/2)*N^(1/4)).

    public int exactly3Divisors(int N)
    {
        int count=0,flag=0;
        for(int i=2;i*i<=N;i++){
            for(int j=2;j*j<=i;j++){
                if(i%j==0){
                    flag=1;
                    break;
                }
            }
            if(flag==0){
                count++;
            }
            flag=0;
        }
        return count;
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X