Çok sayıda alt problemle dinamik programlama


11

Çok sayıda alt problemle dinamik programlama. Bu yüzden Interview Street'ten bu sorunu çözmeye çalışıyorum:

Izgara Yürüyüş (50 puan Skoru)
Bir yer almaktadır konumunda boyutlu ızgara . Izgaranın boyutları ). Bir adımda, boyutlarından herhangi birinde bir adım önde veya arkada yürüyebilirsiniz . (Yani her zaman olası farklı hamle vardır). Izgarayı hiçbir noktada bırakmayacak şekilde adımlarını kaç şekilde uygulayabilirsiniz? Herhangi bir , veya .N(x1,x2,,xN)(D1,D2,,DNN2NMxixi0xi>Di

İlk denemem bu memoized özyinelemeli çözüm oldu:

def number_of_ways(steps, starting_point):
    global n, dimensions, mem
    #print steps, starting_point
    if (steps, tuple(starting_point)) in mem:
        return mem[(steps, tuple(starting_point))]
    val = 0
    if steps == 0:
        val = 1
    else:
        for i in range(0, n):
            tuple_copy = starting_point[:]
            tuple_copy[i] += 1
            if tuple_copy[i] <= dimensions[i]:
                val += number_of_ways(steps - 1, tuple_copy)
            tuple_copy = starting_point[:]
            tuple_copy[i] -= 1
            if tuple_copy[i] > 0:
                val += number_of_ways(steps - 1, tuple_copy)
    mem[(steps, tuple(starting_point))] = val
    return val

Büyük sürpriz: bellek yetersizliği nedeniyle çok sayıda adım ve / veya boyut için başarısız oluyor.

Bir sonraki adım dinamik programlama kullanarak çözümümü geliştirmek. Ama başlamadan önce yaklaşımla ilgili büyük bir sorun görüyorum. Bağımsız değişken starting_pointbir bir -tuple, kadar büyüktür . Yani aslında, fonksiyon olabilir ile .n 10 1 x i100nn10number_of_ways(steps, x1, x2, x3, ... x10)1xi100

Ders kitaplarında gördüğüm dinamik programlama problemlerinin neredeyse hepsinde twp değişkenleri var, böylece sadece iki boyutlu bir matris gerekli. Bu durumda, on boyutlu bir matrise ihtiyaç duyulacaktır. Yani toplamda hücre.10010

Dinamik programlamada 2-B matrisler ile, hesaplamalar genellikle sadece önceki satır dolayısıyla uzamsal karmaşıklığını azaltarak, bir sonraki hesaplama için gerekli olan için . Bu durumda nasıl yapacağımı bilmiyorum. Bir tabloyu görselleştirmek mümkün değildir, bu nedenle cevabın doğrudan yukarıdaki yinelemeden gelmesi gerekecektir.mnmin(m,n)

GÜNCELLEME

Peter Shor'un önerilerini kullanmak ve bazı küçük düzeltmeler yapmak, özellikle işlevindeki konumu takip etmek ve boyutları iki A ve B setine bölmek yerine, bölmeyi yinelemeli olarak, etkili bir şekilde kullanarak kümede yalnızca bir boyutun bulunduğu bir temel duruma ulaşılana kadar böl ve fethetme yöntemi.W(i,ti)

Tüm testleri maksimum yürütme süresinin altında geçen aşağıdaki uygulama ile geldim:

def ways(di, offset, steps):
    global mem, dimensions
    if steps in mem[di] and offset in mem[di][steps]:
        return mem[di][steps][offset]
    val = 0
    if steps == 0:
        val = 1
    else:
        if offset - 1 >= 1:
            val += ways(di, offset - 1, steps - 1)
        if offset + 1 <= dimensions[di]:
            val += ways(di, offset + 1, steps - 1)
    mem[di][steps][offset] = val
    return val


def set_ways(left, right, steps):
    # must create t1, t2, t3 .. ti for steps
    global mem_set, mem, starting_point
    #print left, right
    #sleep(2)
    if (left, right) in mem_set and steps in mem_set[(left, right)]:
        return mem_set[(left, right)][steps]
    if right - left == 1:
        #print 'getting steps for', left, steps, starting_point[left]
        #print 'got ', mem[left][steps][starting_point[left]], 'steps'
        return mem[left][steps][starting_point[left]]
        #return ways(left, starting_point[left], steps)
    val = 0
    split_point =  left + (right - left) / 2 
    for i in xrange(steps + 1):
        t1 = i
        t2 = steps - i
        mix_factor = fact[steps] / (fact[t1] * fact[t2])
        #print "mix_factor = %d, dimension: %d - %d steps, dimension %d - %d steps" % (mix_factor, left, t1, split_point, t2)
        val += mix_factor * set_ways(left, split_point, t1) * set_ways(split_point, right, t2)
    mem_set[(left, right)][steps] = val
    return val

import sys
from time import sleep, time

fact = {}
fact[0] = 1
start = time()
accum = 1
for k in xrange(1, 300+1):
    accum *= k
    fact[k] = accum
#print 'fact_time', time() - start

data = sys.stdin.readlines()
num_tests = int(data.pop(0))
for ignore in xrange(0, num_tests):
    n_and_steps = data.pop(0)
    n, steps = map(lambda x: int(x), n_and_steps.split())
    starting_point = map(lambda x: int(x), data.pop(0).split())
    dimensions = map(lambda x: int(x), data.pop(0).split())
    mem = {}
    for di in xrange(n):
        mem[di] = {}
        for i in xrange(steps + 1):
            mem[di][i] = {}
            ways(di, starting_point[di], i)
    start = time()
    #print 'mem vector is done'
    mem_set = {}
    for i in xrange(n + 1):
        for j in xrange(n + 1):
            mem_set[(i, j)] = {}
    answer = set_ways(0, n, steps)
    #print answer
    print answer % 1000000007
    #print time() - start

2
"çok sayıda adım ve / veya boyut için başarısız" - burada "başarısız" ne anlama geliyor?
Raphael

1
Hoşgeldiniz! Sorunuzu, a) uygun Markdown ve LaTeX biçimlendirmesini (lütfen gelecekte kendiniz için) kullanın ve b) gereksiz oluğu kaldırın. C kodu bulanıklığını önemsemiyoruz; lütfen kendinizi fikirlerle sınırlayın , bu merkezi şeylerin sahte kodu.
Raphael

Başarısız olması, mem[]sözlüğü doldurarak kullanılabilir tüm sistem belleğini tüketir . Cevabımı temizlediğiniz için teşekkür ederim. LaTeX'e çok aşina değil, bir dahaki sefere çaba gösterecek.
Alexandre

Düzenleyici kutusunun yanındaki Markdown'da yardım bulabilirsiniz; LaTeX'in astarı için buraya bakınız .
Raphael

Yanıtlar:


14

Farklı boyutlar bağımsızdır . Ne yapabilirsiniz her boyut için, hesaplamak olduğunu j almak sadece bu boyutta var, kaç farklı yürür adımlarını. O sayıya diyelim . Sorunuzdan, bu sayıları dinamik programlama ile nasıl hesaplayacağınızı zaten biliyorsunuz.W ( j , t )tW(j,t)

Şimdi, boyutunda adımlarını yürüyüş sayısını saymak kolaydır . Sen var böylece boyutta atılan adımlar toplam sayısı boyutlarını interspersing yollarını olan ve bu yöntemin her biri için sahip olduğunuz yürür. Bu fazla miktar almak için Şimdi, bellek kontrol altında, çünkü sadece değerlerini hatırlamanız gerekiyor . Büyük için zaman süperpolinom olarak büyür , ancak çoğu bilgisayarın hafızadan çok daha fazla zamanı vardır. i ( NtiiitiΠN1W(i,ti)t1+t2++tN=M(M(Nt1,t2,,tM)itiΠ1NW(i,ti)W(j,t)N

t1+t2++tN=M(Mt1,t2,,tN) Πi=1NW(i,ti).
W(j,t)N

Daha da iyisini yapabilirsiniz. Boyutları ve olarak iki alt kümeye yinelemeli olarak bölün ve yalnızca alt kümesindeki boyutları ve sadece boyutları kullanarak kaç yürüyüş olduğunu hesaplayın . Bu numaralara ve . Toplam yürüyüş sayısınıB A B W A ( t ) W B ( t )ABABWA(t)WB(t)

t1+t2=M(Mt1)WA(t1)WB(t2).

Merhaba Peter. Tamam, eksik olan içgörüydü. Artık sadece bir şüphem kaldı. Dış toplam, M'ye göre tüm olası t1, t2, ... tn kombinasyonları üzerinde yinelenir. Ne yazık ki, bu tür kombinasyonların sayısı C (M + 1, N-1) olup C (300) kadar yüksek olabilir. +1, 10-9). Çok sayıda ... :(
Alexandre

1
@Alexandre: İkinci algoritmam ("Daha iyisini yapabilirsin" ile başlayarak) bu problemi yaşamıyor. İlk algoritmayı cevabımda bıraktım çünkü ilk bulduğum algoritma ve ikinci algoritmayı birincinin bir varyantı olarak açıklamak, motivasyon olmadan vermekten çok daha kolay.
Peter Shor

İkinci algoritmayı uyguladım. Daha hızlı, ancak yine de en büyük sınırlar için çok düşük. Birincisi ile ilgili sorun, M'ye toplanan tüm t1, t2, t3, ... tn olasılıkları üzerinde yinelemekti. İkinci algoritma sadece t1 + t2 = M'ye çözümler üzerinde yineliyordu. Ancak aynı şey Wa için de yapılmalıdır. (t1), t1 '+ t2' = t1'e çözeltilerin tekrarlanması. Ve böylece özyinelemeli. İlgilenmeniz durumunda uygulama: pastebin.com/e1BLG7Gk . Ve ikinci algoritmada, multinomial t1 üzerinde M olmalı, t2 hayır?
Alexandre

Boşver! Çözüldü! Set_ways işlevinde de not kullanmak zorunda kaldı. İşte hızlı yanan son çözüm! pastebin.com/GnkjjpBN Anlayışınız için teşekkür ederiz Peter. Her iki kilit gözlemi de yaptınız: problem bağımsızlığı ve bölün ve fethet. İnsanların çözümüme bakmalarını öneriyorum çünkü yukarıdaki cevapta olmayan bazı şeyler var, örneğin üçüncü bir argümana ihtiyaç duyan W (i, ti) işlevi, konum. Bu, i, ti ve konum değer kombinasyonları için hesaplanmalıdır. Mümkünse, ikinci algoritmanızdaki multinomial t2'yi de ekleyin.
Alexandre

4

için bir formül (iç hücre için, sınır durumlarını yok sayan):now(s,x1,,xn)

now(s,x1,,xn)=+i=0nnow(s1,x1,,xi1,xi+1,xi+1,,xn)+i=0nnow(s1,x1,,xi1,xi1,xi+1,,xn)

İşte bazı fikirler.

  • için tüm değerleri hesapladıktan sonra , için tüm hesaplanan değerleri düşürebileceğinizi görüyoruz .s=ks<k
  • Sabit bir , tablo girişlerini sözlükbilimsel sırayla hesaplamanız gerekir (sadece basit olduğu için). Daha sonra, her hücrenin sadece bir "yarıçapı" içinde bu tür hücrelere ihtiyacı olduğunu, yani koordinatın bir hücreden daha uzakta olamayacağını unutmayın. Bu nedenle, yinelemeniz değerine , için tüm değerleri düşürebilirsiniz . Bu yeterli değilse, için aynı şeyi - Sabit için ile, damla değerlerine ve ulaşıldığında - vb.sx1=ix1i2x2x1=ix1=ix2j2x2=j
  • "Bu nedenle her zaman olası farklı hareket vardır" ifadesinin yalnızca ızgaranın ortasında olduğunu, yani tüm için ve olduğunu unutmayın . Ama yanıt kolaydır orta olduğunu da araçlarının: bu sadece . Çalışan bir dinamik programlama yinelemeniz varsa, bu tek başına tablonun çoğunu tıraş etmenize izin verecektir ( ).2NxiM>0xi+M<Dii(2N)MMN
  • Dikkat edilmesi gereken başka bir şey, tüm tabloyu hesaplamanıza gerek olmadığıdır; çoğu değer yine de ile doldurulur ( ). Kendinizi etrafındaki kenar uzunluğundaki (hiper) küple sınırlandırabilirsiniz ( ızgaradan çıkan yollar nedeniyle eğileceğini unutmayın).0MN2Mx

Bellek kullanımını oldukça düşük tutmak için bu yeterli olmalıdır.


Merhaba Raphael, diyelim ki hedefimiz şimdi (3, 3, 3, 3), 5x5x5 ızgarada. Dinamik programlamayı kullanarak ve önerdiğiniz gibi lex sırasını kullanarak, şimdi (0, 0, 0, 0), sonra (0, 0, 0, 1), ... şimdi (0, 5, 5, 5) hesaplayacağız. Şimdi hangi noktayı atabiliriz (0, 0, 0, 0) (bu, (5, 5, 5) 'den bir yarıçaptan daha fazladır, çünkü şimdi hesaplamak için buna ihtiyacımız olacak (1, 0, 0) , 0), şimdi (1, 0, 0, 1), vb? M << N'den birkaç kez bahsettiniz, ancak sınırlar 1 <= M <= 300 ve 1 <= N <= 10. , aşırı uçlarda, 1 << 300 gibi görünmüyor.
Alexandre

1) İkinci mermimde belirsiz olan nedir? Hesaplar hesaplamaz , atabilirsiniz . Yine de bu atabileceğiniz en eski nokta değil ; ihtiyacınız olan son hücre . 2) ve için dürüst olmak gerekirse sizin spesifik değerlerinizden fazla endişe etmiyorum . Genel soruna bakmayı tercih ederim. yoksa , son iki mermi size çok yardımcı olmaz. Bununla birlikte, ve , etkiyi fark etmek için yeterli olmalı ve hiçbir strateji acı vermiyor. (2,0,0,0)(0,\*,\*,\*)(0,0,0,0)(1,0,0,0)MNMNM=1N=10
Raphael

1
1) kurşun anlıyorum. Bu, mekansal karmaşıklığı M * D ^ N'den D ^ N'ye düşürür, ancak D ^ N hala çok fazladır. 2) merminin nasıl çalıştığını tam olarak görmüyorum. Yorumumdaki örneği örneklemek için kullanabilir misiniz?
Alexandre

@Alexandre önceki yorumda yaptım. Okumayı Eğer anlamında boşluk karmaşıklığını azaltır kez daha sonra, ikinci nokta uygulama, , ikinci kez ve yakında. (Daha doğrusu, bu gider için vb.)max i = 1 , , N D i D N - 1 D N - 2N i = 1 D i N i = 2 D iDmaxi=1,,NDiDN1DN2i=1NDii=2NDi
Raphael

Nasıl yapılacağını tam olarak anlamadım ... Diyelim ki anladım ve mekansal karmaşıklığı D'ye düşürdüm. Temel olarak, M * D ^ N alt problemlerinin hala çözülmesi gerekmiyor mu? Sorunu polinom haline getirmek için ek bir özelliğe ihtiyaç duyulmuyor mu?
Alexandre
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.