L'objectif est simple: calculer la fonction de totient pour autant de nombres que possible en 10 secondes et additionner les nombres.
Vous devez imprimer votre résultat à la fin et vous devez réellement le calculer. Aucune fonction de totient automatisé n'est autorisée, mais les bibliothèques de bignum le sont. Vous devez commencer à 1 et compter tous les entiers consécutivement. Vous n'êtes pas autorisé à sauter des numéros.
Votre score est le nombre de nombres que votre programme peut calculer sur votre machine / combien mon programme peut calculer sur votre machine . Mon code est un programme simple en C ++ (optimisations désactivées), j'espère que vous pourrez l'exécuter.
Propriétés importantes que vous pourriez utiliser!
- si
gcd(m,n) = 1, phi(mn) = phi(m) * phi(n)
- si
p
est premier,phi(p) = p - 1
(pourp < 10^20
) - si
n
c'est pair,phi(2n) = 2 phi(n)
- d'autres répertoriés dans le premier lien
Mon code
#include <iostream>
using namespace std;
int gcd(int a, int b)
{
while (b != 0)
{
int c = a % b;
a = b;
b = c;
}
return a;
}
int phi(int n)
{
int x = 0;
for (int i=1; i<=n; i++)
{
if (gcd(n, i) == 1)
x++;
}
return x;
}
int main()
{
unsigned int sum = 0;
for (int i=1; i<19000; i++) // Change this so it runs in 10 seconds
{
sum += phi(i);
}
cout << sum << endl;
return 0;
}
1, 3, 5, 2, 4
ou similaire?Réponses:
Nimrod: ~ 38 667 (580 000 000/15 000)
Cette réponse utilise une approche assez simple. Le code utilise un tamis à nombre premier simple qui stocke le nombre premier de la plus petite puissance principale dans chaque emplacement pour les nombres composites (zéro pour les nombres premiers), puis utilise la programmation dynamique pour construire la fonction totient sur la même plage, puis résume les résultats. Le programme passe pratiquement tout son temps à construire le tamis, puis calcule la fonction totient en une fraction du temps. Il semble que cela revient à construire un tamis efficace (avec la légère torsion que l'on doit également être en mesure d'extraire un facteur premier pour les nombres composites du résultat et doit maintenir l'utilisation de la mémoire à un niveau raisonnable).
Mise à jour: performances améliorées en réduisant l'empreinte mémoire et en améliorant le comportement du cache. Il est possible d'extraire 5 à 10% de performances supplémentaires, mais l'augmentation de la complexité du code n'en vaut pas la peine. En fin de compte, cet algorithme exerce principalement un goulot d'étranglement von Neumann d'un processeur, et il y a très peu de réglages algorithmiques qui peuvent contourner cela.
A également mis à jour le diviseur de performances car le code C ++ n'était pas censé être compilé avec toutes les optimisations et personne d'autre ne l'a fait. :)
Mise à jour 2: opération de tamisage optimisée pour un meilleur accès à la mémoire. Maintenant, gérer les petits nombres premiers en masse via memcpy () (~ 5% d'accélération) et ignorer les multiples de 2, 3 et 5 lors du tamisage des nombres premiers plus grands (~ 10% d'accélération).
Code C ++: 9,9 secondes (avec g ++ 4.9)
Code Nimrod: 9,9 secondes (avec -d: release, backend gcc 4.9)
la source
Java, score ~ 24 000 (360 000 000/15 000)
Le code java ci-dessous effectue le calcul de la fonction totient et du tamis principal ensemble. Notez que selon votre machine, vous devez augmenter la taille de tas initiale / maximale (sur mon ordinateur portable plutôt lent, je devais monter
-Xmx3g -Xms3g
).la source
Nimrod: ~ 2 333 333 (42 000 000 000/18 000)
Cela utilise une approche entièrement différente de ma réponse précédente. Voir les commentaires pour plus de détails. Le
longint
module se trouve ici .la source
2*sqrt(n)
), ce qui donne un score beaucoup plus faible.C #: 49 000 (980 000 000/20 000)
/codegolf//a/26800 "Code d'Howard".
Mais les valeurs phi modifiées sont calculées pour les entiers impairs.
Nouveau score: 61 000 (1 220 000
000/20 000) Dans "App.config", j'ai dû ajouter "gcAllowVeryLargeObjects enabled = true".
la source
Python 3: ~ 24 000 (335 000 000/14 000)
Ma version est un port Python de l'algorithme de Howard . Ma fonction d'origine était une modification d'un algorithme introduit dans ce blog .
J'utilise les modules Numpy et Numba pour accélérer le temps d'exécution. Notez que normalement, vous n'avez pas besoin de déclarer les types de variables locales (lorsque vous utilisez Numba), mais dans ce cas, je voulais réduire le temps d'exécution autant que possible.
Édition: fonctions de construction et de synthèse combinées en une seule fonction.
C ++: 9,99 s (n = 14 000); Python 3: 9,94 s (n = 335 000 000)
la source
Voici mon implémentation Python qui semble être capable de lancer ~ 60000 nombres en 10 secondes. Je factorise les nombres en utilisant l'algorithme Rho Pollard et en utilisant le test de primalité Rabin Miller.
la source
φ (2 n ) = 2 n - 1
Σ φ (2 i ) = 2 i - 1 pour i de 1 à n
Tout d'abord, quelque chose à trouver des temps:
Pour le code de référence, pour moi, c'est:
Maintenant, Haskell:
Il fait quelque chose avec 2525224 chiffres en 0,718 seconde. Et maintenant, je remarque juste le commentaire de @ Howard.
la source
Matlab: 1464 = 26355867/18000
Je ne peux pas tester votre code, j'ai donc divisé par 18000 car il représente l'ordinateur le plus rapide de ceux qui l'ont testé. Je suis arrivé au score en utilisant cette propriété:
J'aime surtout que ce soit une doublure:
la source
phi(p)
pour tous les non-primep
?Python 2.7: 10.999 (165975/15090)
Pypy 2.3.1: 28,496 (430000/15090)
Quelques méthodes intéressantes que j'utilise:
Test de Pseudoprime fort de Rabin-Miller - Un test de primalité qui fournit un algorithme probabiliste efficace pour déterminer si un nombre donné est premier
Formule de produit d'Euler - Le produit est sur les nombres premiers distincts divisant n
Code:
la source