Factorielles divisibles

Récemment, j'ai été complÚtement déconcerté par ce tweet de Farm Library:


"C'est ce qui arrive si vous ne multipliez pas mais divisez dans la factorielle."

Quand je l'ai vu, j'ai dĂ» abandonner mon entreprise, prendre un cahier et vĂ©rifier la formule. Le projet de rĂ©sultat semblait logique. Depuis la version multiplicative n ! avec l'augmentation n tend vers l'infini, alors la version "divisante" devrait tendre vers zĂ©ro. Et  f r a c n 2 n ! se comporte de cette façon; fonction polynomiale n 2 croĂźt plus lentement qu'une fonction de puissance n ! pour assez grand n :

 frac11, frac42, frac96, frac1624, frac25120, frac36720, frac495040, frac6440320, frac81362880, frac1003628800


Mais pourquoi le rĂ©sultat de la division prend-il la forme  fracn2n! ? D'oĂč ça vient n2 ?

Pour rĂ©pondre Ă  cette question, j'ai dĂ» dĂ©manteler l'ancien traumatisme associĂ© Ă  l'Ă©tude de la division fractionnaire, mais j'ai fait face Ă  la douleur. En passant de la formule du tweet de gauche Ă  droite, nous obtenons d'abord  fracnn−1 . Ensuite, en divisant cette valeur par n−2 nous obtenons

 cfrac fracnn−1n−2= fracn(n−1)(n−2)


En poursuivant dans cette voie, on se retrouve avec:

n mathbin/(n−1) mathbin/(n−2) mathbin/(n−3) mathbin/ cdots mathbin/1= fracn(n−1)(n−2)(n−3) cdots1= fracn(n−1)!


Pour obtenir le rĂ©sultat affichĂ© dans le tweet  fracn2n! , nous multiplions simplement le numĂ©rateur et le dĂ©nominateur par n . (Bien qu'Ă  mon goĂ»t, l'expression  fracn(n−1)! plus clair.)



Je suis un fan factoriel officiellement reconnu. Gardez vos sĂ©quences de Fibonacci avec vous; voici ma fonctionnalitĂ© prĂ©fĂ©rĂ©e. Chaque fois que j'apprends un nouveau langage de programmation, mon premier exercice consiste Ă  Ă©crire plusieurs procĂ©dures de calcul des factorielles. Au fil des ans, j'ai trouvĂ© plusieurs variantes de ce sujet, par exemple, un remplacement dans la dĂ©finition  fois sur + (ce qui nous donne des nombres triangulaires). Mais il semble que je n'ai jamais pensĂ© Ă  remplacer avant  fois sur  mathbin/ . Cela se rĂ©vĂšle Ă©trange. La multiplication Ă©tant commutative et associative, on peut dĂ©finir n! tout comme le produit de tous les entiers de 1 avant n sans se soucier de l'ordre des opĂ©rations. Mais lors de la division, l'ordre ne peut ĂȘtre ignorĂ©. Dans le cas gĂ©nĂ©ral x mathbin/y ney mathbin/x et (x mathbin/y) mathbin/z nex mathbin/(y mathbin/z) .

Dans le tweet de Farm Library, les sĂ©parateurs sont en ordre dĂ©croissant: n,n−1,n−2, ldots,1 . De toute Ă©vidence, cela sera remplacĂ© par un ordre croissant: 1,2,3, ldots,n . Que se passe-t-il si nous dĂ©finissons la factorielle de division comme 1 mathbin/2 mathbin/3 mathbin/ cdots mathbin/n ? Un autre retour Ă  l'algorithme de division fractionnaire de l'Ă©cole nous donne une rĂ©ponse simple:

1 mathbin/2 mathbin/3 mathbin/ cdots mathbin/n= frac12 times3 times4 ti m e s c d o t s t i m e s n   = f r a c 1 n ! 


En d'autres termes, lorsque nous divisons plusieurs fois, en comptant 1 avant n , le rĂ©sultat final sera Ă©gal Ă  la rĂ©ciproque n ! . (Je voudrais mettre un point d'exclamation Ă  la fin de cette phrase, mais hĂ©las!) Si vous cherchez une rĂ©ponse canonique Ă  la question «Qu'obtenons-nous en divisant au lieu de multiplier en n ! ? ", Alors je dirais que  f r a c 1 n ! Est un meilleur candidat que  f r a c n ( n - 1 ) ! . Pourquoi n'acceptons-nous pas la symĂ©trie entre n ! et sa valeur inverse?

Bien sûr, il existe de nombreuses autres façons de placer n valeurs entiÚres dans l'ensemble \ {1 \ ldots n \} . Mais combien exactement? Comme il s'est avéré, exactement n ! ! Par conséquent, il peut sembler n ! façons uniques de définir une fonction de division n ! . Cependant, l'étude des réponses des deux permutations ci-dessus nous fait comprendre qu'un modÚle plus simple fonctionne ici. Quel que soit l'élément de la séquence qui apparaßt en premier, il apparaßt au numérateur d'une grande fraction, et le produit de tous les autres éléments est le dénominateur. Par conséquent, à la fin, il n'y a que n résultats différents (en supposant que nous effectuons toujours des opérations de division strictement de gauche à droite). Pour tout entier k dans la gamme de 1 avant n en définissant k au début de la ligne, nous créons une division n ! égal à k divisé par tous les autres facteurs. Vous pouvez écrire ceci comme suit:

 cfrack fracn!k, textquipeutĂȘtreconvertien frack2n!


Et donc nous avons rĂ©solu une petite Ă©nigme sur la façon dont dans ce tweet  fracn(n−1)! transformĂ© en  fracn2n! .
Il convient de noter que toutes ces fonctions convergent vers zĂ©ro lorsque n Ă  l'infini. D'un point de vue asymptotique,  frac12n!, frac22n!, ldots, fracn2n! identique.



Oui! Mission accomplie. Le problÚme est résolu. Le travail est terminé. Maintenant, nous savons tout ce dont nous avons besoin pour diviser les factoriels, non?

Eh bien, il y a peut-ĂȘtre encore une question. Que dira l'ordinateur? Si nous prenons notre algorithme factoriel prĂ©fĂ©rĂ©, et faisons ce qui est proposĂ© dans un tweet, en remplaçant toutes les occurrences de l'opĂ©rateur  fois (ou * ) le / , que se passera-t-il? Lequel de n options de division n! le programme nous donnera-t-il?

Voici mon algorithme préféré pour calculer les factorielles en tant que programme sur Julia :

 function mul!(n) if n == 1 return 1 else return n * mul!(n - 1) end end 

Cet algorithme a introduit des gĂ©nĂ©rations entiĂšres de nerds au concept de rĂ©cursivitĂ©. Sous forme de texte, il indique: si n est Ă©gal 1 alors mul!(n) est Ă©gal 1 . Sinon, vous devez calculer la fonction mul!(n−1) puis multipliez le rĂ©sultat par n .

Vous pouvez demander ce qui se passe si n sera nul ou nĂ©gatif. Vous pouvez demander, mais il vaut mieux ne pas le faire. Pour nos objectifs actuels n in mathbbN .

Commençant par tout positif n , la séquence d'appels récursifs tombera tÎt ou tard sur n=1 .

Une fonction peut ĂȘtre Ă©crite plus succinctement en utilisant le style de dĂ©finition Julia sur une seule ligne:

 mul!(n) = n == 1 ? 1 : n * mul!(n - 1) 

La partie droite de l'opérateur d'affectation est-elle une expression conditionnelle ou un opérateur ternaire de la forme a ? b : c a ? b : c . Voici a la condition booléenne du test, qui doit renvoyer true ou false . Si a est true , l'expression b évaluée et le résultat devient la valeur de l'expression entiÚre. Sinon, c calculé.

Juste pour m'assurer que j'ai tout bien fait, voici les 10 premiers factoriels calculés par ce programme:

 [mul!(n) for n in 1:10] 10-element Array{Int64,1}: 1 2 6 24 120 720 5040 40320 362880 3628800 

Maintenant, modifions cette définition et transformons la seule occurrence * dans / , en laissant tout le reste inchangé (à l'exception du nom de la fonction).

 div!(n) = n == 1 ? 1 : n / div!(n - 1) 

Et c'est ce que le programme retournera si nous l'exécutons pour les valeurs n de 1 avant 20 $ :

 [div!(n) for n in 1:20] 20-element Array{Real,1}: 1 2.0 1.5 2.6666666666666665 1.875 3.2 2.1875 3.657142857142857 2.4609375 4.063492063492063 2.70703125 4.432900432900433 2.9326171875 4.773892773892774 3.14208984375 5.092152292152292 3.338470458984375 5.391690662278897 3.523941040039063 5.675463855030418 

Quoi? Ce n'est certainement pas comme converger vers zĂ©ro, tout comme  frac1n! ou  fracnn−1 . En fait, les valeurs ne ressemblent pas Ă  cela, car elles ne vont pas converger. A en juger par le graphique ci-dessous, la sĂ©quence se compose de deux composantes intermittentes, dont chacune semble croĂźtre lentement vers l'infini, et s'Ă©carte Ă©galement de l'autre.


Comprenant ce que nous observons ici, il sera utile de changer le type de sortie de la fonction div! . Au lieu d'utiliser l'opérateur de division / , qui renvoie la valeur sous forme de nombre à virgule flottante, nous pouvons le remplacer par l'opérateur // , qui renvoie la valeur rationnelle exacte, arrondie au terme le plus bas.

 div!(n) = n == 1 ? 1 : n // div!(n - 1) 

Voici une séquence de valeurs pour n 1:20 :

 20-element Array{Real,1}: 1 2//1 3//2 8//3 15//8 16//5 35//16 128//35 315//128 256//63 693//256 1024//231 3003//1024 2048//429 6435//2048 32768//6435 109395//32768 65536//12155 230945//65536 262144//46189 

La liste regorge de modĂšles intĂ©ressants. Il s'agit d'une double hĂ©lice dans laquelle les nombres pairs et impairs en zigzags se dĂ©placent dans des threads complĂ©mentaires. Les nombres pairs ne sont pas seulement pairs, ils sont tous des degrĂ©s 2 . De plus, ils apparaissent par paires - d'abord au numĂ©rateur, puis au dĂ©nominateur - et leur sĂ©quence n'est pas dĂ©croissante. Mais il y a des lacunes; tous les diplĂŽmes ne sont pas prĂ©sents 2 . Le fil impair semble encore plus complexe, diffĂ©rents petits coefficients simples apparaissent et disparaissent dans les nombres. (Les nombres premiers doivent ĂȘtre petits, au moins moins n .)

Ce résultat m'a surpris. Je m'attendais à voir une séquence beaucoup plus douce, comme celles que j'ai calculées sur papier. Tous ces sauts brisés de haut en bas n'avaient aucun sens. La tendance générale à une croissance illimitée du ratio n'avait pas non plus de sens. Comment diviser constamment, tout en recevant de plus en plus de nombres?

À ce stade, vous pouvez interrompre la lecture et essayer de trouver votre propre thĂ©orie sur l'origine de ces nombres en zigzag. Si vous avez besoin d'un indice, alors vous l'avez, et un trĂšs fort, presque un spoiler: recherchez une sĂ©quence de numĂ©rateurs ou une sĂ©quence de dĂ©nominateurs dans l'EncyclopĂ©die en ligne des sĂ©quences entiĂšres .



Voici un autre indice. Un petit changement dans le programme div! convertit complÚtement la sortie. Modifiez simplement la derniÚre expression en remplaçant n // div!(n - 1) par div!(n - 1) // n .

 div!(n) = n == 1 ? 1 : div!(n - 1) // n 

Maintenant, les résultats ressemblent à ceci:

 10-element Array{Real,1}: 1 1//2 1//6 1//24 1//120 1//720 1//5040 1//40320 1//362880 1//3628800 

C'est la fonction inverse de la factorielle que nous avons dĂ©jĂ  vue, une sĂ©rie de valeurs gĂ©nĂ©rĂ©es en allant de gauche Ă  droite dans une sĂ©quence croissante de diviseurs 1 mathbin/2 mathbin/3 mathbin/ cdots mathbin/n .

Sans surprise, la modification de la derniÚre expression d'une procédure modifie le résultat. Au final, on sait que la division n'est ni commutative ni associative. Mais il est difficile de comprendre pourquoi la séquence de valeurs générée par le programme d'origine donne une forme en zigzag si étrange. Quel mécanisme donne naissance à de telles puissances appariées de deux et alternant des valeurs impaires et paires?

J'ai trouvé que l'explication de ce qui se passe dans une séquence en zigzag est plus facile sur une version itérative de la procédure, plutÎt que sur une version récursive. (Cette déclaration peut sembler ennuyeuse à ceux qui trouvent les définitions récursives plus simples, mais cela vient de se produire.) Voici à quoi ressemble le programme:

 function div!_iter(n) q = 1 for i in 1:n q = i // q end return q end 

Je dĂ©clare que cette procĂ©dure avec un cycle fonctionnel est identique Ă  une fonction rĂ©cursive, en ce sens que si div!(n) et div!_iter(n) renvoient un rĂ©sultat pour un entier positif n , alors ce sera toujours le mĂȘme. Voici ma preuve:

 [div!(n) for n in 1:20] [div!_iter(n) for n in 1:20] 1 1//1 2//1 2//1 3//2 3//2 8//3 8//3 15//8 15//8 16//5 16//5 35//16 35//16 128//35 128//35 315//128 315//128 256//63 256//63 693//256 693//256 1024//231 1024//231 3003//1024 3003//1024 2048//429 2048//429 6435//2048 6435//2048 32768//6435 32768//6435 109395//32768 109395//32768 65536//12155 65536//12155 230945//65536 230945//65536 262144//46189 262144//46189 

Pour comprendre le processus qui gĂ©nĂšre ces nombres, considĂ©rez les valeurs sĂ©quentielles des variables i et q chaque fois que vous exĂ©cutez une boucle. À l'origine i et q sont Ă©gaux 1 ; par consĂ©quent, aprĂšs la premiĂšre passe du cycle, l'expression q = i // q donne q valeur  frac11 . Alors i=2 et q= frac11 c'est-Ă -dire une nouvelle signification q est Ă©gal  frac21 . Dans la troisiĂšme itĂ©ration i=3 et q= frac21 cela nous donne  fraciq rightarrow frac32 . Si cela est toujours dĂ©routant, alors imaginez  fraciq comment i times frac1q . Une observation importante ici est qu'avec chaque cycle de boucle q obtient la valeur opposĂ©e, devenant  frac1q .

Si vous développez ces opérations et examinez les multiplications et les divisions incluses dans chaque élément de la série, un schéma apparaßt:

 frac11, quad frac21, quad frac1 cdot32, quad frac2 cdot41 cdot3, quad frac1 cdot3 cdot52 cdot4 quad frac2 cdot4 cdot61 cdot3 cdot5



Sous forme générale:

 frac1 cdot3 cdot5 cdot cdots cdotn2 cdot4 cdot cdots cdot(n−1) quad( textimpairn) qquad frac2 cdot4 cdot6 cdot cdots cdotn1 cdot3 cdot5 cdot cdots cdot(n−1) quad( textevenn)




Les fonctions 1 cdot3 cdot5 cdot cdots cdotn pour bizarre n et 2 cdot4 cdot6 cdot cdots cdotn pour mĂȘme n avoir leur propre nom! Ils sont appelĂ©s doubles factoriels, et sont Ă©crits comme n!! .

Terminologie horrible, non? Ce serait mieux s'ils étaient appelés «semi-factoriels». Et si je ne savais pas, je lirais n!! comme factorielle factorielle.

Le double factoriel n est dĂ©fini comme le produit de n et de tous les entiers positifs plus petits de la mĂȘme paritĂ©. Donc, notre curieuse sĂ©quence de valeurs en zigzag est juste  fracn!!(n−1)!! .

Un article de 2012 de Henry W. Gould et Jocelyn Quentens (hélas, derriÚre le mur payant) explore l'utilisation des factorielles doubles. Ils sont beaucoup plus courants que vous ne le pensez. Au milieu du XVIIe siÚcle, John Wallis a dérivé l'identité suivante:

 frac pi2= frac2 cdot2 cdot4 cdot4 cdot6 cdot6 cdots1 cdot3 cdot3 cdot5 cdot5 cdot7 cdots= limn rightarrow infty frac((2n)!!)2(2n+1)!!(2n−1)!!


Une sĂ©rie encore plus Ă©trange impliquant un cube de valeurs factorielles doubles est rĂ©sumĂ©e dans  frac2 pi . Il a Ă©tĂ© dĂ©couvert par nul autre que Srinivasa Ramanujan.

Gould et Kientens ont également considéré l'équivalent factoriel double pour les coefficients binomiaux. Le coefficient binomial standard est défini comme:

 binomnk= fracn!k!(n−k)!


La version double ressemble Ă  ceci:

 left( binomnk right)= fracn!!k!!(n−k)!!


Notez que nos nombres en zigzag correspondent Ă  cette description, et peuvent donc ĂȘtre considĂ©rĂ©s comme des coefficients binomiaux de factorielles doubles. Plus prĂ©cisĂ©ment, ce sont de tels nombres:

 left( binomn1 right)= left( binomnn−1 right)= fracn!!1!!(n−1)!!


Haricot nature  binomn1 pas trĂšs intĂ©ressant; il est juste Ă©gal n . Mais la version double  gauche( binomn1 droite) comme nous l'avons vu, une danse plus vivante danse. Et contrairement au binĂŽme habituel, il n'est pas toujours entier. (Les seules valeurs entiĂšres sont 1 et 2 .)

Un regard sur les nombres en zigzag en tant que quotient de factorielles doubles explique un certain nombre de leurs propriĂ©tĂ©s, Ă  commencer par l'alternance de valeurs paires et impaires. Nous pouvons Ă©galement voir pourquoi tous les nombres pairs de la sĂ©quence sont des puissances de 2. ConsidĂ©rons l'exemple avec n=6 . Le numĂ©rateur de cette fraction est 2 cdot4 cdot6=48 recevoir de 6 $ multiplicateur 3 . Mais le dĂ©nominateur est 1 cdot3 cdot5=15 . Les triplets au-dessus et en dessous se rĂ©trĂ©cissent, nous laissant  frac165 . De telles rĂ©ductions se produisent dans chaque cas. Chaque fois qu'un facteur impair apparaĂźt dans la sĂ©quence des nombres pairs m , il a nĂ©cessairement la forme 2 cdotm mais Ă  ce moment-lĂ  mĂȘme m devrait dĂ©jĂ  apparaĂźtre dans une sĂ©quence de nombres impairs.



Une sĂ©quence de nombres en zigzag est-elle une rĂ©ponse raisonnable Ă  la question: «Que se passe-t-il si nous divisons plutĂŽt que n! ? " Ou le programme informatique qui les gĂ©nĂšre s'est-il avĂ©rĂ© ĂȘtre un algorithme erronĂ©? À mon avis personnel,  frac1n! - une rĂ©ponse plus intuitive, mais  fracn!!(n−1)!! - plus intĂ©ressant.

De plus, l'existence mĂȘme d'une sĂ©quence en zigzag Ă©largit nos horizons. Comme indiquĂ© ci-dessus, si vous insistez pour que l'algorithme de division passe toujours dans l'ordre dans la liste des numĂ©rateurs n , Ă  chaque Ă©tape en divisant le nombre Ă  gauche par le nombre Ă  droite, il y a un total n rĂ©sultats possibles, et ils se ressemblent tous. Mais la solution en zigzag offre des possibilitĂ©s beaucoup plus larges. On peut formuler le problĂšme comme suit: prendre l'ensemble des numĂ©rateurs \ {1 \ dots n \} , choisissez son sous-ensemble et inversez tous les Ă©lĂ©ments de ce sous-ensemble; Maintenant, nous multiplions tous les numĂ©rateurs, Ă  la fois inverses et directs. Si le sous-ensemble inversĂ© est vide, le rĂ©sultat sera une factorielle rĂ©guliĂšre n! . Si tous les numĂ©rateurs sont devenus inverses Ă  leurs valeurs, alors nous obtenons le contraire  frac1n! . Et si chaque deuxiĂšme numĂ©rateur est converti, en commençant par n−1 , alors le rĂ©sultat sera un Ă©lĂ©ment d'une sĂ©quence en zigzag.

Ce ne sont lĂ  que quelques-unes des nombreuses options disponibles; au total il y a 2n sous-ensembles de n Ă©lĂ©ments. Par exemple, vous pouvez prendre l'inverse de chaque nombre qui est premier ou une puissance principale (2,3,4,5,7,8,9,11, points) . Au petit n les rĂ©sultats sautent, mais restent constamment infĂ©rieurs Ă  1 :


Cependant, si je continuais ce tableau pour plus n , il décollait dans la stratosphÚre. Les degrés des nombres premiers deviennent trÚs clairsemés sur la droite numérique.



Je vais maintenant poser une question. Nous avons vu des variations factorielles approchant zĂ©ro comme n Ă  l'infini par exemple 1/n! . Nous avons vu d'autres variations croĂźtre avec l'augmentation n illimitĂ©, y compris moi-mĂȘme n! et les nombres en zigzag. Existe-t-il des variĂ©tĂ©s du processus factoriel qui convergent vers une frontiĂšre finie qui n'est pas nulle?

Tout d'abord, l'algorithme suivant m'est venu Ă  l'esprit:

 function greedy_balance(n) q = 1 while n > 0 q = q > 1 ? q /= n : q *= n n -= 1 end return q end 

Nous parcourons des valeurs entiĂšres de n jusqu'Ă  1 calculer le produit / quotient actuel dans le processus q . À chaque Ă©tape, si la valeur actuelle q plus 1 , nous le divisons par le numĂ©rateur suivant, sinon, nous effectuons la multiplication. Ce schĂ©ma met en Ɠuvre une sorte de gestion de la rĂ©troaction ou un comportement de recherche cible. Si q devenant trop gros, nous le rĂ©duisons; s'il est trop petit, nous l'augmentons. J'ai suggĂ©rĂ© que tout en s'efforçant n Ă  l'infini q convergera vers une gamme de valeurs en constante diminution Ă  cĂŽtĂ© de 1 .

Mais l'expérience m'a donné une autre surprise:


Une telle vague en dents de scie n'est pas tout Ă  fait ce Ă  quoi je m'attendais. Curieusement, la courbe n'est pas symĂ©trique autour 1 ; les Ă©carts par rapport au dessus ont une amplitude plus grande que par le dessous. Mais cette distorsion est plus visuelle que mathĂ©matique. Depuis q est privĂ©, Ă  distance de 1 avant 10 $ identique Ă  la distance de 1 avant  frac110 mais sur une Ă©chelle linĂ©aire ne ressemble pas Ă  ça. Vous pouvez rĂ©soudre ce problĂšme en compilant un graphique logarithmique du quotient:


Maintenant, le graphique est symĂ©trique, ou au moins approximativement le mĂȘme, et centrĂ© par rapport Ă  la valeur 0 qui est le logarithme 1 . Mais il reste un secret plus sĂ©rieux. La vague en dents de scie est trĂšs rĂ©guliĂšre et a une pĂ©riode 4 , sans montrer de signes de compression dans le sens de la valeur limite attendue  logq=0 . Les valeurs numĂ©riques suggĂšrent que lorsque n Ă  l'infini, les pics de la courbe convergent vers une valeur lĂ©gĂšrement supĂ©rieure q= frac53 , et les bas approchent d'une valeur lĂ©gĂšrement infĂ©rieure q= frac35 . (Logarithmes de base correspondants 10 $ sont approximativement Ă©gaux  pm0.222 ) Je n'ai pas pu comprendre pourquoi cela se produit. Peut-ĂȘtre que quelqu'un peut expliquer.

Un échec avec cet algorithme gourmand ne signifie pas que nous ne pouvons pas diviser la q=1 .

Si nous travaillons avec les logarithmes des numérateurs, alors cette procédure devient le cas d'un problÚme de calcul bien connu appelé le «problÚme de la division d'un ensemble de nombres». On nous donne de nombreux nombres réels et nous devons les diviser en deux ensembles dont la somme est égale ou aussi proche que possible de l'égalité. Il s'agit d'une tùche qui s'est avérée difficile, mais elle est également appelée ( PDF ) «la tùche complexe la plus simple».

Pour tout n nous pouvons constater qu'en utilisant les valeurs inverses d'un autre sous-ensemble de numérateurs nous donne une meilleure approximation de n!=1 . Pour les petits n nous pouvons résoudre ce problÚme par la force brute: il suffit de tout considérer 2n sous-ensembles et choisissez le meilleur.

J'ai calculé les partitions optimales jusqu'à n=30 lorsque vous devez choisir parmi un milliard d'options.


De toute Ă©vidence, le graphique devient plus plat. Vous pouvez utiliser la mĂȘme mĂ©thode pour forcer la convergence vers toute autre valeur comprise entre 0 avant n! .

Et donc nous obtenons une autre réponse à la question posée par le tweet et commençons notre voyage. Que se passe-t-il si nous divisons et ne nous multiplions pas n! ? Tout ce que nous voulons.

Source: https://habr.com/ru/post/fr440512/


All Articles