A combinatória desempenha um papel importante na ciência da computação. Utilizamos frequentemente métodos combinatórios tanto na análise quanto no design de algoritmos. Por exemplo, um método para encontrar uma cobertura -vertex definida em um gráfico pode apenas inspecionar todos os \ subconjuntos possíveis \ binom {n} {k} . Enquanto as funções binomiais crescem exponencialmente, se k é alguma constante fixa, terminamos com um algoritmo de tempo polinomial por análise assintótica.
Muitas vezes, problemas da vida real exigem mecanismos combinatórios mais complexos que podemos definir em termos de recorrências. Um exemplo famoso é a sequência de fibonacci (ingenuamente) definida como:
Agora calcular o valor do º prazo cresce exponencialmente usando essa recorrência, mas graças a programação dinâmica, podemos calcular-lo em tempo linear. Agora, nem todas as recorrências se prestam ao DP (mão secundária, a função fatorial), mas é uma propriedade potencialmente explorável ao definir algumas contagens como uma recorrência em vez de uma função geradora.
As funções de geração são uma maneira elegante de formalizar algumas contagens para uma determinada estrutura. Talvez a mais famosa seja a função geradora binomial definida como:
Felizmente, isso tem uma solução de formulário fechado. Nem todas as funções geradoras permitem uma descrição tão compacta.
Agora, minha pergunta é a seguinte: com que frequência as funções geradoras são usadas no design de algoritmos? É fácil ver como eles podem ser explorados para entender a taxa de crescimento exigida por um algoritmo por meio de análise, mas o que eles podem nos dizer sobre um problema ao criar um método para resolver algum problema?
Se muitas vezes a mesma contagem pode ser reformulada como uma recorrência, ela pode se prestar à programação dinâmica, mas, novamente, talvez a mesma função geradora tenha uma forma fechada. Portanto, não é tão uniformemente cortado.
fonte
Respostas:
As funções de geração são úteis ao projetar algoritmos de contagem. Ou seja, não apenas quando você está procurando o número de objetos com uma determinada propriedade, mas também quando você está procurando uma maneira de enumerar esses objetos (e, talvez, gerar um algoritmo para contar os objetos). Há uma apresentação muito boa no capítulo 7 da Matemática Concreta, de Ronald Graham, Donald Knuth e Oren Patashnik . Os exemplos abaixo são desses livros (os erros e a falta de clareza são meus).
Suponha que você esteja procurando maneiras de fazer alterações com um determinado conjunto de moedas. Por exemplo, com denominações americanas comuns¹, as moedas possíveis são . Para dar 42 em mudança, uma possibilidade é ; outra possibilidade é . Escreveremos . De maneira mais geral, podemos escrever uma função geradora para todas as formas de alteração: Em termos mais técnicos, é um termo no espaço da série de potências sobre as cinco variáveis[1],[5],[10],[25],[100] [25][10][5][1][1] [10][10][10][10][1][1] 42⟨[25][10][5][1]2⟩=⟨[10]4[1]2⟩
Um exemplo mais difícil: suponha que você queira estudar todas as maneiras de ladrilhar retângulos com dominós 2 × 1. Por exemplo, existem duas maneiras de ladrilhar um retângulo 2 × 2, com dois dominós horizontais ou com dois dominós verticais. Contar o número de maneiras de ladrilhar um retângulo é bastante fácil, mas o caso se torna rapidamente não-óbvio. Podemos enumerar todas as inclinações possíveis de uma faixa horizontal de altura 3 juntando dominós, o que gera rapidamente padrões repetitivos:2×n 3×n
Mais uma vez, leia Concrete Mathematics para uma apresentação menos apressada³.
¹ Eu sei que minha lista está incompleta; assuma um EUA simplificado adequado para exemplos matemáticos.²
² Além disso, se aparecer, assuma moedas esféricas.
³ E melhor digitado.
fonte
Lembro-me de um problema que tive que resolver durante um concurso de programação para estudantes em 2001. O problema era este:
Comecei com loops aninhados, mas bati rapidamente em uma parede. Então percebi que tinha que começar enumerando o que pode ser feito com as massas mais leves antes de prosseguir com as mais pesadas. Eu poderia resolver o problema com muitos loops não aninhados.
Se eu não tivesse sido jovem arrogante e auto-suficiente naquela época (e eu sabia e praticava funções geradoras), eu poderia ter definido o problema de gerar funções como tal:
Defina como OGF para o número de maneiras que um peso pode ser pesado, dado o conjunto de massas.f(x) n
Que peso no prato direito posso pesar com uma única massa de 1?
Três possibilidades:
Portanto, há uma maneira de pesar , uma maneira de pesar e uma maneira de pesar . A função geradora para essa massa é algo como , que corresponde a:−1 0 1 x−1+1+x
A função geradora de uma única massa é , que é:m x−m+1+xm
Dado um multiset de massas, é expresso como o produto das funções geradoras de massa única:M f
Agora, dado um pacote que pode executar operações em polinômios, você apenas precisa:
E você terminou. Agora seu polinômio tem o número de maneiras de pesar no índice . A única entrada é o multiconjunto de massas .w≥0 w M
Eu projetei o algoritmo usando componentes matematicamente sólidos. A parte principal do algoritmo, que é uma divisão polinomial com o menor grau primeiro, é linear e pode ser implementada por um pacote pronto para uso. Pode não ser o ideal, mas certamente tem um desempenho melhor do que o que fiz no concurso e de maneira menos propensa a erros.
Se você observar atentamente o processo de divisão, verá rapidamente que o restante pode ser visto como o "estado oculto atual" em todos os estados do processo e o quociente como resultado. O processo termina quando o "estado oculto atual" atinge zero em qualquer lugar.
Você pode implementar polinômios como matrizes ou, se eles forem realmente esparsos, como listas ordenadas com coeficiente de índice e isso não mudará o algoritmo.
fonte
Ao desenvolver um algoritmo para maximização submodular monótona sobre um matróide, tivemos que resolver a recorrência Depois de perceber que , reduzimos o problema para calcular alguma sequência universal . O último foi realizado usando funções geradoras e, a partir daí, obtivemos uma fórmula explícita para , novamente usando funções geradoras. Você pode encontrar a solução no artigo se estiver curioso, embora nunca tenhamos nos incomodado em incluir essa derivação.
fonte
Talvez o extenso estudo do Quicksort, e suas muitas variantes, seja o exemplo mais claro. Algumas considerações combinatórias governaram a consideração de alternativas, e a análise das soluções para equações bastante complexas mostra vantagens de desempenho (ou não) delas.
fonte