La combinatoire joue un rôle important en informatique. Nous utilisons fréquemment des méthodes combinatoires à la fois dans l'analyse et la conception dans les algorithmes. Par exemple, une méthode pour trouver un ensemble de couvertures -vertex dans un graphique pourrait simplement inspecter tous les sous-ensembles \ binom {n} {k} possibles. Alors que les fonctions binomiales croissent de façon exponentielle, si k est une constante fixe, nous nous retrouvons avec un algorithme de temps polynomial par analyse asymptotique.
Souvent, les problèmes réels nécessitent des mécanismes combinatoires plus complexes que nous pouvons définir en termes de récurrences. Un exemple célèbre est la séquence de fibonacci (naïvement) définie comme:
Maintenant, le calcul de la valeur du ème terme croît de façon exponentielle en utilisant cette récurrence, mais grâce à la programmation dynamique, nous pouvons le calculer en temps linéaire. Désormais, toutes les récurrences ne se prêtent pas à DP (au contraire, la fonction factorielle), mais c'est une propriété potentiellement exploitable lors de la définition de certains comptes comme une récurrence plutôt qu'une fonction génératrice.
La génération de fonctions est une manière élégante de formaliser un certain nombre pour une structure donnée. La fonction de génération binomiale la plus connue est peut-être la suivante:
Heureusement, cela a une solution sous forme fermée. Toutes les fonctions de génération ne permettent pas une description aussi compacte.
Maintenant, ma question est la suivante: à quelle fréquence les fonctions de génération sont-elles utilisées dans la conception d'algorithmes? Il est facile de voir comment ils peuvent être exploités pour comprendre le taux de croissance requis par un algorithme via l'analyse, mais que peuvent-ils nous dire sur un problème lors de la création d'une méthode pour résoudre un problème?
Si plusieurs fois le même nombre peut être reformulé comme une récurrence, il peut se prêter à une programmation dynamique, mais là encore, la même fonction de génération a peut-être une forme fermée. Il n'est donc pas coupé de façon aussi uniforme.
la source
Réponses:
La génération de fonctions est utile lorsque vous concevez des algorithmes de comptage. Autrement dit, non seulement lorsque vous recherchez le nombre d'objets ayant une certaine propriété, mais également lorsque vous recherchez un moyen d'énumérer ces objets (et, peut-être, de générer un algorithme pour compter les objets). Il y a une très bonne présentation dans le chapitre 7 de Concrete Mathematics par Ronald Graham, Donald Knuth et Oren Patashnik . Les exemples ci-dessous sont tirés de ces livres (les erreurs et le manque de clarté sont les miens).
Supposons que vous cherchiez les moyens de faire des changements avec un ensemble de pièces donné. Par exemple, avec les coupures américaines courantes¹, les pièces possibles sont . Pour donner ¢ 42 en monnaie, une possibilité est ; une autre possibilité est . Nous écrirons . Plus généralement, nous pouvons écrire une fonction génératrice pour toutes les façons d'apporter du changement: En termes plus techniques, est un terme dans l'espace des séries de puissance sur les cinq variables[ 25 ] [ 10 ] [ 5 ] [ 1 ] [ 1 ] [ 10 ] [ 10 ] [ 10 ] [ 10 ] [ 1 ] [ 1 ] 42 ⟨ [ 25 ] [ 10 ][ 1 ] , [ 5 ] , [ 10 ] , [ 25 ] , [ 100 ] [ 25 ] [ 10 ] [ 5 ] [ 1 ] [ 1 ] [ 10 ] [ 10 ] [ 10 ] [ 10 ] [ 1 ] [ 1 ] H = Σ h ≥ 0 Σ q ≥ 0 Σ d ≥ 0 Σ n ≥ 0 Σ p ≥ 0 [ 100 ] h [ 25 ] q [ 10 ] d [ 5 ] n [ 1 ] p42 ⟨ [ 25 ] [ 10 ] [ 5 ] [ 1 ]2⟩ = ⟨ [ 10 ]4[ 1 ]2⟩
Un exemple plus difficile: supposons que vous vouliez étudier toutes les façons de carreler des rectangles avec des dominos 2 × 1. Par exemple, il existe deux façons de carreler un rectangle 2 × 2, soit avec deux dominos horizontaux, soit avec deux dominos verticaux. Compter le nombre de façons de carreler un rectangle est assez facile, mais le cas devient rapidement non évident. Nous pouvons énumérer tous les pavages possibles d'une bande horizontale de hauteur 3 en collant les dominos ensemble, ce qui donne rapidement des motifs répétitifs:2 × n 3 × n
Encore une fois, lisez Concrete Mathematics pour une présentation moins précipitée³.
¹ Je sais que ma liste est incomplète; supposons un US simplifié adapté aux exemples mathématiques.²
² De plus, s'il apparaît, supposons des pièces sphériques.
³ Et une meilleure composition.
la source
Je me souviens d'un problème que j'ai dû résoudre lors d'un concours de programmation étudiante en 2001. Le problème était celui-ci:
J'ai commencé avec des boucles imbriquées, mais j'ai rapidement heurté un mur. Puis j'ai réalisé que je devais commencer par énumérer ce qui peut être fait avec les masses plus légères avant de continuer avec les plus lourdes. Je pourrais résoudre le problème avec beaucoup de boucles non imbriquées.
Si je n'avais pas été arrogant et autosuffisant à ce moment-là (et si j'avais connu et pratiqué des fonctions de génération), j'aurais peut-être défini le problème de la génération de fonctions en tant que telles:
Définissez comme OGF pour le nombre de façons dont un poids peut être pesé compte tenu de l'ensemble des masses.F( x ) n
Quel poids sur le plateau droit puis-je peser avec une seule masse de 1?
Trois possibilités:
Il y a donc une façon de peser , une façon de peser et une façon de peser . La fonction génératrice de cette masse est quelque chose comme , ce qui correspond à:- 1 0 1 X- 1+ 1 + x
La fonction génératrice pour une seule masse est , qui est:m X- m+ 1 + xm
Étant donné un ensemble de masses, est exprimé comme le produit des fonctions génératrices de masse unique:M F
Maintenant, étant donné un package qui peut effectuer des opérations sur des polynômes, il vous suffit de:
Et tu as fini. Votre polynôme a maintenant le nombre de façons de peser à l'indice . La seule entrée est le multi -ensemble de masses .w ≥ 0 w M
J'ai conçu l'algorithme en utilisant des composants mathématiquement solides. La partie principale de l'algorithme, qui est une division polynomiale avec le degré le plus bas en premier, est linéaire et peut être implémentée par un package standard. Ce n'est peut-être pas optimal, mais il fonctionne certainement mieux que ce que j'ai fait au concours, et de manière moins sujette aux erreurs.
Si vous regardez de près le processus de division, vous voyez rapidement que le reste peut être considéré comme "l'état caché actuel" à chaque état du processus, et le quotient comme résultat. Le processus se termine lorsque «l'état caché actuel» atteint zéro partout.
Vous pouvez implémenter des polynômes sous forme de tableaux ou, s'ils sont vraiment très rares, sous forme de listes ordonnées à coefficient d'indexation, et cela ne changera pas l'algorithme.
la source
Lors du développement d'un algorithme de maximisation sous-modulaire monotone sur un matroïde, nous avons dû résoudre la récurrence Après avoir remarqué que , nous avons réduit le problème au calcul d'une séquence universelle . Ce dernier a été accompli en utilisant des fonctions génératrices, et de là nous avons obtenu une formule explicite pour , en utilisant à nouveau des fonctions génératrices. Vous pouvez trouver la solution dans le document si vous êtes curieux, bien que nous n'ayons jamais pris la peine d'inclure cette dérivation.
la source
L'étude approfondie de Quicksort et de ses nombreuses variantes est peut-être l'exemple le plus clair. Là, des considérations combinatoires ont gouverné la prise en compte des alternatives, et l'analyse des solutions à des équations assez complexes en montre les avantages (ou non) en termes de performances.
la source