begin process at 2008 05 16 20:53:31
1 173 738 membres
547 nouveaux aujourd'hui
13 972 membres club

Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum.
Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

CALCUL DES NOMBRES PREMIERS PAR LA CRIBLE D'ÉRATOSTHÈNE


Information sur la source



Description

La crible d'ératosthène permet de trouver les n premiers nombres premiers en parcourant de 2 à n tous les nombres et en supprimant les multiples. Pour en savoir plus :

http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes

La méthode Prime utilise le mot clé yield return.
La méthode Prime2 utilise une stack.
La méthode Prime3 est une optimisation algorithmique.
La méthode Prime4 est Prime5 sont des optimisations algorithmique mais ne sont pas toujours plus rapide ... voir commentaires.

Prime2 est plus rapide mais plus gourmand en mémoire à cause de l'utilisation d'une stack, attention puisqu'on utilise une Stack la méthode nous renvoie les nombres à l'envers.

Source

  • static IEnumerable<int> Prime(int max)
  • {
  • BitArray b = new BitArray(max);
  • b.SetAll(true);
  • b[0] = false;
  • b[1] = false;
  • for (int i = 2; i < max; i++)
  • {
  • if (b[i] == true)
  • {
  • yield return i;
  • for (int j = 2 * i; j < max; j += i)
  • {
  • b[j] = false;
  • }
  • }
  • }
  • }
  • static IEnumerable<int> Prime2(int max)
  • {
  • BitArray b = new BitArray(max);
  • b.SetAll(true);
  • b[0] = false;
  • b[1] = false;
  • Stack<int> primes = new Stack<int>();
  • for (int i = 2; i < max; i++)
  • {
  • if (b[i] == true)
  • {
  • primes.Push(i);
  • for (int j = 2 * i; j < max; j += i)
  • {
  • b[j] = false;
  • }
  • }
  • }
  • return primes;
  • }
  • static IEnumerable<int> Prime3(int max)
  • {
  • BitArray b = new BitArray(max);
  • b.SetAll(true);
  • b[0] = false;
  • b[1] = false;
  • Stack<int> primes = new Stack<int>();
  • for (int i = 2; i < max; i++)
  • {
  • if (b[i] == true)
  • {
  • primes.Push(i);
  • // nécessaire si i > Math.Sqrt(int.MaxValue);
  • long j = (long)i * i;
  • if (j < max)
  • {
  • for (int k = (int)j; k < max; k += i)
  • {
  • b[k] = false;
  • }
  • }
  • }
  • }
  • return primes;
  • }
  • static IEnumerable<int> Prime4(int max)
  • {
  • BitArray b = new BitArray(max);
  • b.SetAll(true);
  • b[0] = false;
  • b[1] = false;
  • Stack<int> primes = new Stack<int>();
  • Boolean stopFor = false;
  • for (int i = 2; i < max; i++)
  • {
  • if (b[i] == true)
  • {
  • primes.Push(i);
  • if (!stopFor)
  • {
  • // nécessaire si i > Math.Sqrt(int.MaxValue);
  • long j = (long)i * i;
  • if (j < max)
  • {
  • for (int k = (int)j; k < max; k += i)
  • {
  • b[k] = false;
  • }
  • }
  • else
  • {
  • stopFor = true;
  • }
  • }
  • }
  • }
  • return primes;
  • }
  • static IEnumerable<int> Prime5(int max)
  • {
  • BitArray b = new BitArray(max);
  • b.SetAll(true);
  • b[0] = false;
  • b[1] = false;
  • Stack<int> primes = new Stack<int>();
  • int pivot = (int)Math.Truncate(Math.Sqrt(max)) + 1;
  • for (int i = 2; i < pivot; i++)
  • {
  • if (b[i] == true)
  • {
  • primes.Push(i);
  • // nécessaire si i > Math.Sqrt(int.MaxValue);
  • long j = (long)i * i;
  • if (j < max)
  • {
  • for (int k = (int)j; k < max; k += i)
  • {
  • b[k] = false;
  • }
  • }
  • }
  • }
  • for (int i = pivot; i < max; i++)
  • {
  • if (b[i] == true)
  • {
  • primes.Push(i);
  • }
  • }
  • return primes;
  • }
        static IEnumerable<int> Prime(int max)
        {
            BitArray b = new BitArray(max);
            b.SetAll(true);

            b[0] = false;
            b[1] = false;

            for (int i = 2; i < max; i++)
            {
                if (b[i] == true)
                {
                    yield return i;
                    for (int j = 2 * i; j < max; j += i)
                    {
                        b[j] = false;
                    }
                }
            }
        }

        static IEnumerable<int> Prime2(int max)
        {
            BitArray b = new BitArray(max);
            b.SetAll(true);

            b[0] = false;
            b[1] = false;

            Stack<int> primes = new Stack<int>();

            for (int i = 2; i < max; i++)
            {
                if (b[i] == true)
                {
                    primes.Push(i);
                    for (int j = 2 * i; j < max; j += i)
                    {
                        b[j] = false;
                    }
                }
            }

            return primes;
        }

        static IEnumerable<int> Prime3(int max)
        {
            BitArray b = new BitArray(max);
            b.SetAll(true);
            b[0] = false;
            b[1] = false;
            Stack<int> primes = new Stack<int>();

            for (int i = 2; i < max; i++)
            {
                if (b[i] == true)
                {
                    primes.Push(i);
                    // nécessaire si i > Math.Sqrt(int.MaxValue); 
                    long j = (long)i * i;
                    if (j < max)
                    {
                        for (int k = (int)j; k < max; k += i)
                        {
                            b[k] = false;
                        }
                    }
                }
            }
            return primes;
        }

        static IEnumerable<int> Prime4(int max)
        {
            BitArray b = new BitArray(max);
            b.SetAll(true);
            b[0] = false;
            b[1] = false;
            Stack<int> primes = new Stack<int>();

            Boolean stopFor = false;
            for (int i = 2; i < max; i++)
            {
                if (b[i] == true)
                {
                    primes.Push(i);

                    if (!stopFor)
                    {
                        // nécessaire si i > Math.Sqrt(int.MaxValue); 
                        long j = (long)i * i;
                        if (j < max)
                        {
                            for (int k = (int)j; k < max; k += i)
                            {
                                b[k] = false;
                            }
                        }
                        else
                        {
                            stopFor = true;
                        }
                    }
                }
            }
            return primes;
        }

        static IEnumerable<int> Prime5(int max)
        {
            BitArray b = new BitArray(max);
            b.SetAll(true);
            b[0] = false;
            b[1] = false;
            Stack<int> primes = new Stack<int>();

            int pivot = (int)Math.Truncate(Math.Sqrt(max)) + 1;

            for (int i = 2; i < pivot; i++)
            {
                if (b[i] == true)
                {
                    primes.Push(i);

                    // nécessaire si i > Math.Sqrt(int.MaxValue); 
                    long j = (long)i * i;
                    if (j < max)
                    {
                        for (int k = (int)j; k < max; k += i)
                        {
                            b[k] = false;
                        }
                    }
                }
            }

            for (int i = pivot; i < max; i++)
            {
                if (b[i] == true)
                {
                    primes.Push(i);
                }
            }

            return primes;
        }
02 décembre 2007 16:24:15 :
Erreur de copier/coller
02 décembre 2007 17:00:07 :
Optimisation en utilisant une stack plutot qu'un yield return
02 décembre 2007 18:05:40 :
Optimisation algorithmique
03 décembre 2007 12:42:58 :
Optimisation algo proposé par billou_13
03 décembre 2007 15:53:25 :
Rajout de Prime5
  • signaler à un administrateur
    Commentaire de billou_13 le 03/12/2007 09:17:34

    Bonjour à toi,

    Très intéressant comme source. Cependant, je ne vois pas trop pourquoi tu n'as pas implémenté la condition suivante (que je viens de lire ^^).
    Citation: http://fr.wikipedia.org/wiki/Crible_d%27%C3%89ratosth%C3%A8ne
    Étape 4. Si le carré du nombre suivant non rayé est inférieur à 20, alors on recommence à l'étape 2 sinon on déclare tous les entiers restants non rayés premiers.

    Il y a un début sur le prime 3. C'est peut-être alors pour des raisons de performance ?

    Bonne journée,

    PS: à quand la source sur le Crible d'Atkin ^^

  • signaler à un administrateur
    Commentaire de Malkuth le 03/12/2007 09:54:18

    Pourquoi ne pas utiliser une queue<T> au lieu d'une stack<T>?
    plus lent?

  • signaler à un administrateur
    Commentaire de jesusonline le 03/12/2007 11:04:33 administrateur CS

    @Malkuth : J'ai testé les 2, Stack<T> est plus rapide que Queue<T>

    @Billou_13 : Prime3 a cet optimisation - je l'ai fait dans un second temps pour simplifier l'algo.
    Je vais essayer d'implémenter Atkin mais je promet rien ;-)

  • signaler à un administrateur
    Commentaire de billou_13 le 03/12/2007 12:22:58

    C'est exact mais je pense que tu peux optimiser un petit peu plus car le test est réalisé pour tous les entiers de sqrt(max) jusqu'à max.
    Hors, lorsque un entier au carré > max , son prochain aussi.
    Alors, tu peux essayer de mettre un booléen pour éviter de faire la multiplication pour tous les autres.  Cela te fera peut-être gagner du temps ? à tester ^^.

    J'ai vu aussi sur le net que beaucoup de code (en c) notamment, mettent en place cette vérification au niveau de la boucle. Ce qui te donnerai
    for (int i = 2; i < Math.Sqrt(int.MaxValue); i++)
    {
    ...
    }
    Mais je pense que tu n'as pas fait cela car tu veux empiler toutes les réponses dans la pile.

    Ps: pour information, le prochain nombre premier trouvé comptant plus de 10 millions de chiffres offrira 100.000$ à son propriétaire ^^
    On se demande bien ce qui peut motiver à payer un nombre aussi cher ? (peut-être pour sortir un bouquin le contenant ...)

  • signaler à un administrateur
    Commentaire de jesusonline le 03/12/2007 12:44:34 administrateur CS

    Bien vu :p
    J'ai rajouté une méthode Prime4. Par contre j'ai eu des effets bizarre, apparement sur les petits nombres < 1000, Prime4 est plus lent que Prime3 ... :-/

  • signaler à un administrateur
    Commentaire de Malkuth le 03/12/2007 15:17:17

    Re ;)
    je m'en doutais pour le queue mais n'ayant pas testé...

    pour accéléré le prime4 pourquoi ne pas faire 2 boucle :
    ex :

    int maxbcl1 = ((int)Math.Sqrt(int.MaxValue))) + 1

    for (int i = 2; i < maxbcl1; i++)
       if (b[i] == true)
       {
          primes.Push(i);
          for (int k = (int)i*i; k < max; k += i)
             b[k] = false;                
       }

    for (int i = maxbcl1; i < max; i++)
       if (b[i] == true)
          primes.Push(i);

    return primes;


    Ca devrait faire pas mal de test en moins à voir aussi si les boucles for sont les plus rapide pour cette algo.

  • signaler à un administrateur
    Commentaire de Malkuth le 03/12/2007 15:20:16

    billou_13 > à vérifié mais il me semble que de mettre le Math.Sqrt(...) dans la condition de boucle, force le recalcul a chaque itération (reflector devrais nous en dire plus).

  • signaler à un administrateur
    Commentaire de jesusonline le 03/12/2007 15:55:22 administrateur CS

    Malkuth : je viens de rajouter Prime5 à partir de ce que tu proposes.

    Pour tester les perfs, j'utilise ce code :

    static void Main(string[] args)
    {

        foreach (var max in new int[] { 1000, 100000, 500000, 1000000, 2000000, 5000000, 10000000 })
        {
            long time1 = 0;
            long time2 = 0;
            for (int i = 0; i < 5; i++)
            {
                Stopwatch sw = Stopwatch.StartNew();
                var n = Prime4(max);
                sw.Stop();
                time1 += sw.ElapsedMilliseconds;

                sw = Stopwatch.StartNew();
                n = Prime5(max);
                sw.Stop();
                time2 += sw.ElapsedMilliseconds;
            }

            if (time1 > time2)
                Console.ForegroundColor = ConsoleColor.Green;
            else
                Console.ForegroundColor = ConsoleColor.Red;

            Console.WriteLine("Pour les nombres premiers inférieurs à {0:N0} : {1:N0}\t{2:N0}", max, time1, time2);

            Console.ForegroundColor = ConsoleColor.Gray;

        }
    }

    Prime3, Prime4, Prime5 sont assez équivalent niveau perf, il faut que je fasse plus de test pour comprendre qui est plus rapide dans tel cas et surtout pourquoi.

  • signaler à un administrateur
    Commentaire de Malkuth le 03/12/2007 16:48:34

    if (j < max)
    {
    ....
    }

    Cette condition n'est plus néscessaire car i*i est toujours inférieur a max dans la premiére boucle.

  • signaler à un administrateur
    Commentaire de billou_13 le 03/12/2007 17:40:54

    @MALKUTH: Je me suis poser la même question concernant le sqrt dans la boucle for. Mais comme je ne suis pas comme saint thomas ^^, j'ai pas testé.
    Cependant, ayant vu que cette boucle étaient souvent constaté sur le net (calcul dans la condition d'un for), je me dis que cela doit surement être optimiser derrière. Si tu as la réponse à cette question, cela m'intéresse beaucoup !

    Bonne soirée à vous,

    Remarque: Tout ces soucis d'optimisation ceci me font penser au jour ou j'ai découvert que ma fonction de tri par ordre alphabétique d'un tableau était super couteuse car je bouclais n² de fois dans le tableau (je sais, c'est pas beau). Beaucoup de méthodes sont plus sympathiques, pour les curieux: http://interstices.info/display.jsp?id=c_6973

  • signaler à un administrateur
    Commentaire de Malkuth le 03/12/2007 19:01:06

    héhé ^^
    Trouvé une solution jusqu'a plus de 2 fois plus rapide... je vous laisse mariné mais il est question de unsafe.

    billou_13 >

    Effectivement on trouve souvent se genre de contruction avec des calcule dans la condition mais je me demande si se n'est pas de la "parresse" à ne pas vouloir se tapé une variable locale intermédaire.
    En tout cas, toute personne qui utilise cette construction sans avoir vérifié précédement ce qui sortai comme code aprés compilation commetrait une grosse erreur :
    imagine que la condition de limite supérieur prenne une demi-seconde de calcule à elle seule et qu'on la repette 500 fois où plus alors que rien n'en modifie le résultat...
    Par contre si le résultat de cette fonction peut-être modifié dans le temps,
    il faut bien évidément metre le calcul dans la condition, on essais toutefois de réduire ces calcul au max en précalculant les résultats intermediaire.

    Enfin même sans reflector, il est facile de vérifie le comportement de la boucle for :
    for (; MessageBox.Show("", "", MessageBoxButtons.OKCancel) == DialogResult.Cancel; ) ;

Ajouter un commentaire

Appels d'offres

Pub



CalendriCode

Mai 2008
LMMJVSD
   1234
567891011
12131415161718
19202122232425
262728293031 

Téléchargements

Logiciels à télécharger sur le même thème :

Boutique

Boutique de goodies CodeS-SourceS