Algoritmalarda Tekrarlar ve Üretim Fonksiyonları


18

Kombinatorik bilgisayar biliminde önemli bir rol oynar. Algoritmada tasarımın yanı sıra hem analizde hem de kombinasyonel yöntemleri kullanırız. Örneğin , bir grafikte k -vertex kapak kümesini bulmak için bir yöntem yalnızca tümünü inceleyebilir (nk) olası alt kümeler. Binom fonksiyonları katlanarak büyürken,ksabit bir sabitse, asimptotik analizle bir polinom zaman algoritması ile sonuçlanırız.

Çoğu zaman gerçek yaşam problemleri, tekrarlar açısından tanımlayabileceğimiz daha karmaşık kombinasyonel mekanizmalar gerektirir. Ünlü bir örnek (safça) olarak tanımlanan fibonacci dizisidir :

f(n)={1if n=10if n=0f(n1)+f(n2)otherwise

Şimdi değerini hesaplama n inci dönem katlanarak bu tekrarını kullanarak büyür, ama teşekkürler dinamik programlamaya, Lineer zamanda bunu hesaplayabilir. Şimdi, tüm nüksler DP'ye (elden, faktöriyel fonksiyon) ödünç verilmez, ancak bazı sayımları bir üreten fonksiyondan ziyade bir nüks olarak tanımlarken potansiyel olarak sömürülebilir bir özelliktir.

Üreten işlevler, belirli bir yapı için bazı sayıları resmileştirmenin zarif bir yoludur. Belki de en ünlüsü şu şekilde tanımlanan binom üretme fonksiyonudur:

(x+y)α=k=0(αk)xαkyk

Neyse ki bunun kapalı bir form çözümü var. Tüm üretim fonksiyonları böyle kompakt bir açıklamaya izin vermez.

Şimdi sorum şu: algoritma tasarımında üreten fonksiyonlar ne sıklıkla kullanılıyor ? Analiz yoluyla bir algoritmanın ihtiyaç duyduğu büyüme oranını anlamak için nasıl sömürülebileceğini görmek kolaydır, ancak bir sorunu çözmek için bir yöntem oluştururken bize bir sorun hakkında ne söyleyebilirler?

Birçok kez aynı sayı bir nüksetme olarak yeniden formüle edilebilirse, kendini dinamik programlamaya verebilir, ama yine de aynı oluşturma işlevi kapalı bir biçime sahiptir. Yani o kadar düzgün kesilmemiş.


Üreten fonksiyon, nüksü (belki de daha verimli) kullanmak yerine sayıyı hesaplamak için kullanılabilecek bir formül (örneğin, Binet'in fibonacci sayıları formülü) verirse, bunu bir cevap olarak görüyor musunuz?
Aryabhata

Yanıtlar:


11

Sayım algoritmaları tasarlarken, oluşturma işlevleri kullanışlıdır. Yani, sadece belirli bir özelliğe sahip nesnelerin sayısını ararken değil, aynı zamanda bu nesneleri numaralandırmanın bir yolunu ararken (ve belki de nesneleri saymak için bir algoritma oluştururken). Ronald Graham, Donald Knuth ve Oren Patashnik'in Beton Matematiği'nin 7. bölümünde çok iyi bir sunum var . Aşağıdaki örnekler bu kitaplardan alınmıştır (hatalar ve açıklık eksikliği benimdir).

Belirli bir jeton seti ile değişiklik yapmanın yollarını aradığınızı varsayalım. Örneğin, ortak ABD mezhepleri¹ ile, olası paralar . Değişen ¢ 42 vermek için bir olasılık [ 25 ] [ 10 ] [ 5 ] [ 1 ] [ 1 ] ; bir başka olasılık da [ 10 ] [ 10 ] [ 10[1],[5],[10],[25],[100][25][10][5][1][1] . Biz yazacaksýn 42 [ 25 ] [ 10 ] [ 5 ] [ 1 ] 2= [ 10 ] 4 [ 1 ] 2 . Daha genel olarak, değişiklik yapmanın tüm yolları için bir oluşturma işlevi yazabiliriz: H = h 0 q 0 d 0 [10][10][10][10][1][1]42[25][10][5][1]2=[10]4[1]2 Daha teknik terimlerleifade etmek gerekirse,Hkuvvet serisinin beş değişken üzerindeki uzayda bir terimidir[100],[25],[10],[5],[1]. Bu alandaki bir monomerin değerlemesini şu şekilde tanımlayın:

H=h0q0d0n0p0[100]h[25]q[10]d[5]n[1]p
H[100],[25],[10],[5],[1] sonra elde yolları h monomials değişim sent olan sayı olan değerlemesidir hac . Biz ifade edebilen H ilk yolu aşağı yazarak, artımlı bir şekilde P birkaç kuruş vermek değişikliğine sadece, o zaman yollar N
[100]h[25]q[10]d[5]n[1]p=100h+25q+10d+5n+p
vvHPNI
P=I+[1]+[1]2+[1]3+=II[1]N=(I+[5]+[5]2+[5]3+)P=PI[5]D=(I+[10]+[10]2+[10]3+)N=NI[10]Q=(I+[25]+[25]2+[25]3+)D=DI[25]H=(I+[100]+[100]2+[100]3+)Q=QI[100]
If you want to count and not just enumerate the ways to give change, then there is a simple way to use the formal series we've obtained. Apply the homomorphism
S:[1]X,[5]X5,[10]X10,[25]X25,[100]X100
The coefficient of Xv in S(C) is the number of ways to give v cents in change.

A harder example: suppose that you want to study all the ways to tile rectangles with 2×1 dominoes. For example, there are two ways to tile a 2×2 rectangle, either with two horizontal dominoes or with two vertical dominoes. Counting the number of ways to tile a 2×n rectangle is fairly easy, but the 3×n case quickly becomes nonobvious. We can enumerate all the possible tilings of a horizontal band of height 3 by sticking dominoes together, which quickly yields repetitive patterns:

{U=o+LV+ΓΛ+UV=IU+=VΛ=IU+=Λ
where the funny shapes represent elementary domino arrangements: o is no domino, L is a vertical domino on top of the left part of a horizontal domino, I is a vertical domino aligned with the bottom of the band of height 3, = is a horizontal domino aligned with the top of the band plus two horizontal dominoes below it and one step to the right, etc. Here, multiplication represents horizontal concatenation and is not commutative, but there are equations between the elementary patterns that form variables in this power series. As before with the coins, we can substitute X for every domino and get a generating series for the number of tilings of a 3×(2n/3) rectangle (i.e. the coefficient of X3k is the number of ways to tile a rectangle of area 6k, which contains 3k dominoes and has the width 2k). The series can also be used in more versatile ways; for example, by distinguishing vertical and horizontal dominoes, we can count the tilings with a given number of vertical and horizontal dominoes.

Again, read Concrete Mathematics for a less rushed³ presentation.

¹ I know my list is incomplete; assume a simplified US suitable for mathematical examples.²
² Also, if it comes up, assume spherical coins.
³ And better typeset.


8

I remember a problem I had to solve during a student programming contest in 2001. The problem was this one:

Given masses of 1, 7, 13, ... (I don't remember which masses, but there was a finite, determinate set of masses), design a function that determines whether a given weight can be weighed on a scale with this set of masses.

I started with nested loops, but quickly hit a wall. Then I came to realize that I had to start with enumerating what can be done with the lighter masses before going on with the heavier ones. I could solve the problem with a lot of unnested loops.

Hadn't I been youthfully arrogant and self-sufficient at that time (and had I known about and practiced generating functions), I might have defined the problem with generating functions as such :

Define f(x) as the OGF for the number of ways that a weight n can be weighed given the set of masses.

What weight on the right pan can I weigh given a single mass of 1?

Three possibilities:

  • If I put the mass on the left pan, I can weigh 1.
  • If I put the mass on the right pan, I can weigh -1.
  • If I don't use the mass, I can weigh 0.

So there is one way to weigh 1, one way to weigh 0, and one way to weigh 1. The generating function for this mass is something like x1+1+x, which corresponds to:

1x3x(1x)

The generating function for a single mass m is xm+1+xm, which is :

1x3mxm(1xm)

Given a multiset M of masses, f is expressed as the product of the single mass generating functions:

f(x)=mM(1x3m)xmMmmM(1xm)

Now, given a package that can perform operations on polynomials, you just have to :

  • Calculate both products.
  • Perform the division of these products, starting with the lowest degree. (which terminates)
  • Shift the polynomial (euclidian division by xk, keeping the quotient and dumping the remainder)

And you're done. Now your polynomial has the number of ways to weigh w0 at index w. The only input is the multiset of masses M.

I designed the algorithm using mathematically sound components. The main part of the algorithm, which is a polynomial division with lowest degree first, is linear and can be implemented by an off-the-shelf package. It may not be optimal, but it certainly performs better than what I did at the contest, and in a less error-prone way.

If you look closely at the division process, you quickly see that the remainder can be seen as the "current hidden state" at every state of the process, and the quotient as the result. The process terminates when the "current hidden state" reaches zero everywhere.

You can implement polynomials as arrays or, if they are really really sparse, as index-coefficient ordered lists, and this won't change the algorithm.


3

While developing an algorithm for monotone submodular maximization over a matroid, we had to solve the recurrence

γ+1(m)=(2m)γ(m)+(m+1)γ1(m),γ0(m)=1,γm+1(m)=e.
After noticing that γ(m)=m(γ(m1)γ1(m1)), we reduced the problem to computing some universal sequence γ(0). The latter was accomplished using generating functions, and from there we got an explicit formula for γ(m), again using generating functions. You can find the solution in the paper if you're curious, though we never bothered to include this derivation.

0

Perhaps the extensive study of Quicksort, and its many variants, is the clearest example. There combinatoric considerations governed the consideration of alternatives, and analyzing the solutions to quite complex equations shows performance advantages (or not) of them.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.