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

:
[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
Voici une séquence de valeurs pour
n 1:20
:
20-element Array{Real,1}: 1 2
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)
Maintenant, les résultats ressemblent à ceci:
10-element Array{Real,1}: 1 1
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
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

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

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

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.