Compter les groupes abéliens d'une taille donnée

14

Contexte

La dernière fois, nous avons compté des groupes d'une taille donnée , ce qui n'est pas un problème trivial.

Cette fois, nous ne compterons que les groupes abéliens , c'est-à-dire les groupes avec une opération commutative. Formellement, un groupe (G, *) est abélien si x * y = y * x pour tous pour x, y dans G .

Le problème devient beaucoup plus simple de cette façon, nous allons donc les compter efficacement.

Tâche

Écrivez un programme ou une fonction qui accepte un entier non négatif n en entrée et imprime ou renvoie le nombre de groupes abéliens non isomorphes d'ordre n .

Une façon de calculer le nombre de groupes - que nous désignerons par A (n) - est d'observer ce qui suit:

  • A (0) = 0

  • Si p est un nombre premier, A (p k ) est égal au nombre de partitions entières de k . (cf. OEIS A000041 )

  • Si n = mk , et m et k sont co-premiers, A (n) = A (m) A (k) .

Vous pouvez utiliser cette méthode ou toute autre méthode de calcul de A (n) .

Cas de test

Input               Output
0                   0
1                   1
2                   1
3                   1
4                   2
5                   1
6                   1
7                   1
8                   3
9                   2
10                  1
11                  1
12                  2
13                  1
14                  1
15                  1
16                  5
17                  1
18                  2
19                  1
20                  2
4611686018427387904 1300156
5587736968198167552 155232
9223371994482243049 2

(extrait de OEIS A000688 )

Règles supplémentaires

  • Avec suffisamment de temps, de RAM et une taille de registre pouvant contenir l'entrée, votre code devrait fonctionner (en théorie) pour des entiers arbitrairement grands.

  • Votre code doit fonctionner pour tous les entiers compris entre 0 et 2 63 - 1 et se terminer en moins de 10 minutes sur ma machine (Intel i7-3770, 16 Go de RAM, Fedora 21).

    Veuillez vous assurer de chronométrer votre code pour les trois derniers cas de test avant de soumettre votre réponse.

  • Les éléments intégrés qui banalisent cette tâche, comme Mathematica FiniteAbelianGroupCount, ne sont pas autorisés.

  • Les fonctions intégrées qui renvoient ou comptent les partitions entières d'un nombre ou les partitions d'une liste ne sont pas autorisées.

  • Les règles de standard s'appliquent.

Dennis
la source
Le système de factorisation principal de Pyth est trop lent pour ce défi - je dois y remédier.
isaacg

Réponses:

3

CJam ( 39 38 octets)

qimF{~M\{_ee{~\)<1b}%1+a\+}*0=1be&}%:*

Démo en ligne

Cela suit la ligne suggérée de trouver une factorisation première ( mF) puis de calculer les partitions de chaque puissance et de prendre leur produit.

Il existe deux cas particuliers pour mF : il tient compte au 0fur 0^1et à 1mesure 1^1. Ce dernier ne nécessite pas de traitement particulier: il existe un groupe abélien de taille 1 et une partition de 1. Cependant, zéro nécessite un cas particulier.

Le comptage des partitions utilise une récurrence pour A008284(n, k), le nombre de partitions de ndansk parties. Dans OEIS, il est donné

T(n, k) = Sum_{i=1..k} T(n-k, i), for 1<=k<=n-1; T(n, n) = 1 for n >= 1.

mais je pense qu'il est plus utile de considérer la somme comme allant de 1à min(k, n-k).

Dissection

q~              e# Parse input into an integer
mF              e# Factorise it
{               e# For each factor p^a
  ~             e#   Split the array [p a]
                e#   The following lines count partitions of a
                e#   (Note: they would be buggy if a were ever 0, but it isn't)
  M\{           e#   Starting with a table of zero rows, repeat a times
    _ee         e#     Copy table and pair each row with its index
    {~\)<1b}%   e#     Extract that prepended index and use it to sum for each j
                e#     the first jth items of row j
    1+          e#     Append a 1 for P(i, i)
    a\+         e#     Prepend the new row to the table (which is stored in reverse)
  }*
  0=1b          e#   Sum the elements in the latest (first) row

  e&            e#   If p was 0 then replace with 0
}%
:*              e# Take the product
Peter Taylor
la source
5

CJam, 50 49 47 43 octets

ri_mF{1=_L{1$0>{,f{):X-Xj}:+}{;!}?}2j}%:*e&

Utilise la mFfactorisation intégrée de CJam et un port mémorisé de cette fonction de numéro de partition Python:

p=lambda n,x:n==0 or n>0and sum(p(n+~a,a+1)for a in range(x))

ou non golfé:

def p(n, x): # Call like p(n, n). n is number remaining, x is max part size
  if n > 0:
    return sum(p(n-a-1,a+1)for a in range(x))
  else:
    return (n==0)

Comme la réponse de @ RetoKoradi, le dernier cas prend environ 17 secondes sur l'interpréteur hors ligne, car c'est le temps qu'il faut à CJam pour factoriser le nombre. C'est pourquoi je l'ai laissé de côté suite de tests en ligne .

Explication complète

[Main body]
ri                                Read input and convert to int
  _          e&                   Logical AND input with final result to special case 0 
   mF                             Factorise input into [base, exponent] pairs
     {...}%                       Map, converting each pair to a partition number
           :*                     Take product

[Pair -> partition]
1=_                               Get exponent and copy (n,x in above Python)
   L                              Initialise empty cache
    {                       }2j   Memoise with 2 arguments
     1$0>                         Check if n > 0
         {            }{  }?      Execute first block if yes, else second block
                        ;!        Return (n == 0)
          ,f{      }              For each a in range(x) ...
             ):X-Xj               Call p(n-a-1,a+1) recursively
                    :+            Sum the results
Sp3000
la source
4

Mathematica, 96 94 88 octets

f=1##&@@#&;f[SeriesCoefficient[1/f[1-x^Range@#],{x,0,#}]&/@Last/@FactorInteger@#]Sign@#&

Je ne suis pas très compétent avec Mathematica, mais j'ai pensé que j'essaierais. Merci à @ MartinBüttner pour -6 octets.

Cela utilise la formule de fonction de génération pour les partitions entières.

Sp3000
la source
3

CJam, 58 octets

li_mF{1=_L{_1>{_2$<{\;_j}{\,f{)_@\-j}:+}?}{;;1}?}2j}%:*\g*

Essayez-le en ligne

Le tout dernier exemple de test prend une éternité (ou au moins plus longtemps que je ne voulais attendre) dans l'interpréteur en ligne, mais se termine en 17 secondes avec la version hors ligne de CJam sur mon ordinateur portable. Tous les autres exemples de test sont à peu près instantanés.

Cela utilise le CJam mF opérateur , qui donne la factorisation principale avec les exposants. Le résultat est alors le produit du nombre de partitions pour chaque exposant.

La partie principale du code consiste à calculer le nombre de partitions. J'ai implémenté l'algorithme récursif sur la page wikipedia , en utilisant l' jopérateur qui prend en charge la récursivité avec la mémorisation.

Explication:

li    Get input and convert to int.
_     Make a copy to handle 0 special case at the end.
mF    Factorization with exponents.
{     Loop over factors.
  1=    Take exponent from [factor exponent] pair.
  _     Repeat it, recursive calls are initiated with p(n, n).
  L     Empty list as start point of memoization state.
  {     Start recursive block. Argument order is (m, n), opposite of Wikipedia.
    _1>   Check for n > 1.
    {     Start n > 1 case.
      _2$   Copy both m and n.
      <     Check for n < m.
      {     n < m case.
        \;    Pop m.
        _     Copy n.
        j     Make the p(n, n) recursive call.
      }     End n < m case.
      {     Main part of algorithm that makes recursive calls in loop.
        \,    Generate [0 1 ... m-1] range for k.
        f{    Start loop over k.
          )     Increment, since k goes from 1 to m.
          _     Copy k.
          @\    Rotate n to top, and swap. Now have k n k at top of stack.
          -     Subtract, now have k n-k at top of stack.
          j     Make the p(n-k, k) recursive call.
        }     End loop over k.
        :+    Sum up all the values.
      }?    Ternaray operator for n < m condition.
    }     End n > 1 case.
    {     n <= 1 case.
      ;;1   Pop m, n values, and produce 1 as result.
    }?    Ternary operator for n > 1 condition.
  }2j   Recursive call with memoization, using 2 values.
}%    End loop over factors.
:*    Multiply all values.
\     Swap original input to top.
g     Signum.
*     Multiply to get 0 output for 0 input.
Reto Koradi
la source