Ce golf nécessite un calcul factoriel divisé en plusieurs threads ou processus.
Certaines langues facilitent la coordination que d'autres, c'est donc langagnostique. Un exemple de code non golfé est fourni, mais vous devez développer votre propre algorithme.
Le but du concours est de voir qui peut proposer l'algorithme factoriel multicœur le plus court (en octets, pas en secondes) pour calculer N! tel que mesuré par les votes à la fin du concours. Il devrait y avoir un avantage multicœur, nous allons donc exiger qu'il fonctionne pour N ~ 10 000. Les électeurs devraient voter contre si l'auteur ne fournit pas d'explication valable sur la façon dont il répartit le travail entre les processeurs / noyaux et voter en fonction de la concision du golf.
Par curiosité, veuillez publier quelques numéros de performance. Il peut y avoir un compromis performance / score de golf à un moment donné, optez pour le golf tant qu'il répond aux exigences. Je serais curieux de savoir quand cela se produit.
Vous pouvez utiliser des bibliothèques de gros nombres entiers à noyau unique normalement disponibles. Par exemple, perl est généralement installé avec bigint. Cependant, notez que le simple fait d'appeler une fonction factorielle fournie par le système ne divisera normalement pas le travail sur plusieurs cœurs.
Vous devez accepter de STDIN ou ARGV l'entrée N et la sortie vers STDOUT la valeur N !. Vous pouvez éventuellement utiliser un deuxième paramètre d'entrée pour fournir également le nombre de processeurs / cœurs au programme afin qu'il ne fasse pas ce que vous verrez ci-dessous :-) Ou vous pouvez concevoir explicitement pour 2, 4, tout ce dont vous disposez.
Je posterai mon propre exemple de perl oddball ci-dessous, précédemment soumis sur Stack Overflow sous Algorithmes factoriels dans différentes langues . Ce n'est pas du golf. De nombreux autres exemples ont été soumis, dont beaucoup de golf mais pas beaucoup. En raison de la licence de partage, n'hésitez pas à utiliser le code dans tous les exemples du lien ci-dessus comme point de départ.
La performance dans mon exemple est terne pour un certain nombre de raisons: elle utilise trop de processus, trop de conversion chaîne / bigint. Comme je l'ai dit, c'est un exemple intentionnellement étrange. Il en calculera 5000! en moins de 10 secondes sur une machine à 4 cœurs ici. Cependant, une doublure plus évidente pour la boucle suivante / peut faire 5000! sur l'un des quatre processeurs en 3.6s.
Vous devrez certainement faire mieux que cela:
#!/usr/bin/perl -w
use strict;
use bigint;
die "usage: f.perl N (outputs N!)" unless ($ARGV[0] > 1);
print STDOUT &main::rangeProduct(1,$ARGV[0])."\n";
sub main::rangeProduct {
my($l, $h) = @_;
return $l if ($l==$h);
return $l*$h if ($l==($h-1));
# arghhh - multiplying more than 2 numbers at a time is too much work
# find the midpoint and split the work up :-)
my $m = int(($h+$l)/2);
my $pid = open(my $KID, "-|");
if ($pid){ # parent
my $X = &main::rangeProduct($l,$m);
my $Y = <$KID>;
chomp($Y);
close($KID);
die "kid failed" unless defined $Y;
return $X*$Y;
} else {
# kid
print STDOUT &main::rangeProduct($m+1,$h)."\n";
exit(0);
}
}
Mon intérêt pour cela est simplement (1) de soulager l'ennui; et (2) apprendre quelque chose de nouveau. Ce n'est pas un problème de devoirs ou de recherche pour moi.
Bonne chance!
Réponses:
Mathematica
Une fonction parallèle:
Où g est
Identity
ouParallelize
selon le type de processus requisPour le test de synchronisation, nous allons légèrement modifier la fonction, afin qu'elle renvoie l'heure réelle de l'horloge.
Et nous testons les deux modes (de 10 ^ 5 à 9 * 10 ^ 5): (seulement deux noyaux ici)
Résultat:
la source
Haskell:
209200198177 caractères176167 source +3310 drapeau du compilateurCette solution est assez idiote. Il applique le produit en parallèle à une valeur de type
[[Integer]]
, où les listes internes comportent au maximum deux éléments. Une fois que la liste externe est réduite à 2 listes au maximum, nous l'aplatissons et prenons le produit directement. Et oui, le vérificateur de type a besoin de quelque chose d'annoté avec Integer, sinon il ne compilera pas.(N'hésitez pas à lire la partie médiane
f
entreconcat
ets
comme "jusqu'à ce que j'aime la longueur")Les choses semblaient être très bonnes, car le parMap de Control.Parallel.Strategies facilite la mise à jour de ces données sur plusieurs threads. Cependant, il semble que GHC 7 nécessite 33 caractères dans les options de ligne de commande et les variables d'environnement pour permettre au runtime threadé d'utiliser plusieurs cœurs (que j'ai inclus dans le total).A moins que je manque quelque chose, ce qui est certainementpossible. ( Mise à jour : le runtime GHC threadé semble utiliser N-1 threads, où N est le nombre de cœurs, donc pas besoin de manipuler les options d'exécution.)Compiler:
Le temps d'exécution, cependant, était plutôt bon compte tenu du nombre ridicule d'évaluations parallèles déclenchées et que je n'ai pas compilé avec -O2. Pour 50000! sur un MacBook double cœur, j'obtiens:
Temps total pour quelques valeurs différentes, la première colonne est le parallèle joué, la seconde est la version séquentielle naïve:
Pour référence, la version séquentielle naïve est celle-ci (qui a été compilée avec -O2):
la source
Rubis - 111 + 56 = 167 caractères
Il s'agit d'un script à deux fichiers, le fichier principal (
fact.rb
):le fichier supplémentaire (
f2.rb
):Prend simplement le nombre de processus et le nombre à calculer sous forme d'arguments et divise le travail en plages que chaque processus peut calculer individuellement. Multiplie ensuite les résultats à la fin.
Cela montre vraiment à quel point Rubinius est plus lent pour YARV:
Rubinius:
Ruby1.9.2:
(Notez l'extra
0
)la source
inject(:+)
. Voici l'exemple de la documentation:(5..10).reduce(:+)
.8
endroit où il aurait dû y en avoir*
si quelqu'un avait des problèmes pour exécuter cela.Java,
523 519 434 430429 caractèresLes deux 4 de la dernière ligne correspondent au nombre de threads à utiliser.
50000! testé avec le framework suivant (version non golfée de la version originale et avec un peu moins de mauvaises pratiques - bien qu'il en ait encore beaucoup) donne (sur ma machine Linux à 4 cœurs) des temps
Notez que j'ai répété le test avec un fil pour l'équité car le jit s'est peut-être réchauffé.
Java avec bigints n'est pas le bon langage pour jouer au golf (regardez ce que je dois faire juste pour construire les choses misérables, car le constructeur qui prend beaucoup de temps est privé), mais bon.
Il devrait être complètement évident d'après le code non golfé comment il décompose le travail: chaque thread multiplie une classe d'équivalence modulo le nombre de threads. Le point clé est que chaque thread effectue à peu près la même quantité de travail.
la source
CSharp -
206215 caractèresDivise le calcul avec la fonctionnalité C # Parallel.For ().
Éditer; Verrou oublié
Délais d'exécution:
la source
Perl, 140
Prend
N
de l'entrée standard.Fonctionnalités:
Référence:
la source
Scala (
345266244232214 caractères)Utilisation d'acteurs:
Modifier - suppression des références à
System.currentTimeMillis()
, factoriséa(1).toInt
, changé deList.range
àx to y
Edit 2 - a changé la
while
boucle en afor
, a changé le pli gauche en une fonction de liste qui fait la même chose, en s'appuyant sur des conversions de type implicites de sorte que leBigInt
type à 6 caractères n'apparaît qu'une seule fois, a changé println pour imprimerEdit 3 - a découvert comment faire plusieurs déclarations dans Scala
Edit 4 - diverses optimisations que j'ai apprises depuis que j'ai fait cela
Version non golfée:
la source
Scala-2.9.0 170
non golfé:
La factorielle de 10 est calculée sur 4 cœurs en générant 4 listes:
qui se multiplient en parallèle. Il y aurait eu une approche plus simple pour distribuer les chiffres:
Mais la distribution ne serait pas si bonne - les plus petits nombres finiraient tous dans la même liste, le plus élevé dans une autre, conduisant au calcul plus long dans la dernière liste (pour les N élevés, le dernier thread ne serait pas presque vide , mais contiennent au moins des éléments (N / cores) -cores.
Scala dans la version 2.9 contient des collections parallèles, qui gèrent elles-mêmes l'invocation parallèle.
la source
Erlang - 295 caractères.
Première chose que j'ai jamais écrite à Erlang, donc je ne serais pas surpris si quelqu'un pouvait facilement diviser par deux ceci:
Utilise le même modèle de thread que mon entrée Ruby précédente: divise la plage en sous-plage et multiplie les plages ensemble dans des threads séparés, puis multiplie les résultats dans le thread principal.
Je n'ai pas réussi à comprendre comment faire fonctionner escript, alors enregistrez simplement
f.erl
et ouvrez erl et exécutez:avec des substitutions appropriées.
J'ai obtenu environ 8 secondes pour 50000 en 2 processus et 10 secondes pour 1 processus, sur mon MacBook Air (double cœur).
Remarque: je viens de remarquer qu'il se fige si vous essayez plus de processus que le nombre à factoriser.
la source