बड़ी संख्या में उपप्रकारों के साथ गतिशील प्रोग्रामिंग


11

बड़ी संख्या में उपप्रकारों के साथ गतिशील प्रोग्रामिंग। इसलिए मैं इंटरव्यू स्ट्रीट से इस समस्या को हल करने की कोशिश कर रहा हूं:

ग्रिड चलना (स्कोर 50 अंक)
आप स्थिति ( x 1 , x 2 , , x N ) पर एक N आयामी ग्रिड में स्थित हैं । ग्रिड के आयाम हैं ( D 1 , D 2 , , D N )। एक चरण में, आप में से किसी एक में आगे या पीछे एक कदम चल सकते हैं आयाम। (इसलिए हमेशा संभव विभिन्न चालें हैं)। कितने तरीकों से आप ले सकते हैं(x1,x2,,xN)(डी1,डी2,...,डीएनएन2एनएक्स मैं एक्स मैं0 एक्स मैं > डी मैंऐसे कदम जो आप किसी भी बिंदु पर ग्रिड को नहीं छोड़ते हैं? यदि आप किसी भी , या तो या लिए ग्रिड ।एक्समैंएक्समैं0एक्समैं>डीमैं

मेरा पहला प्रयास इस पुनरावर्ती समाधान था:

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

बड़ा आश्चर्य: यह स्मृति की कमी के कारण बड़ी संख्या में चरणों और / या आयामों के लिए विफल रहता है।

इसलिए अगला कदम डायनामिक प्रोग्रामिंग का उपयोग करके मेरे समाधान में सुधार करना है। लेकिन शुरू करने से पहले, मैं दृष्टिकोण के साथ एक बड़ी समस्या देख रहा हूं। तर्क starting_pointएक है n -tuple, जहां n के रूप में बड़ा है 10 । तो वास्तव में, समारोह हो सकता है number_of_ways(steps, x1, x2, x3, ... x10)के साथ 1एक्समैं100

पाठ्यपुस्तकों में मैंने जो गतिशील प्रोग्रामिंग समस्याएं देखीं, उनमें लगभग सभी में दो चर हैं, ताकि केवल दो आयामी मैट्रिक्स की आवश्यकता हो। इस मामले में, एक दस-आयामी मैट्रिक्स की आवश्यकता होगी। तो 10010 कुल कोशिकाएं।

nमिनट(,n)

अपडेट करें

पीटर शोर के सुझावों का उपयोग करना, और कुछ मामूली सुधार करना, विशेष रूप से फ़ंक्शन में स्थिति का ट्रैक रखने की आवश्यकता है , और केवल दो सेट ए और बी में आयामों को विभाजित करने के अलावा, विभाजन को पुनरावर्ती रूप से करना, प्रभावी ढंग से उपयोग करना एक आधार मामले तक पहुँचने के लिए, जहां केवल एक आयाम सेट में होता है, तब तक फूट डालो और जीतो विधि।डब्ल्यू(मैं,टीमैं)

मैं निम्नलिखित कार्यान्वयन के साथ आया, जिसने अधिकतम निष्पादन समय के नीचे सभी परीक्षण पास किए:

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
"यह बड़ी संख्या में चरणों और / या आयामों के लिए विफल रहता है" - यहाँ "असफल" का क्या अर्थ है?
राफेल

1
स्वागत हे! मैंने आपके प्रश्न को a) उचित मार्काडाउन और LaTeX फॉर्मेटिंग (कृपया भविष्य में स्वयं के लिए कृपया) और b) का उपयोग करते हुए सतही नाली को हटा दिया। हम सी-कोड के धब्बों की परवाह नहीं करते हैं; कृपया खुद को विचारों तक सीमित रखें , यह केंद्रीय चीजों का छद्म कोड है।
राफेल

विफलताओं का अर्थ है कि यह mem[]शब्दकोश को भरकर सभी उपलब्ध सिस्टम मेमोरी को समाप्त कर देता है । और मेरे जवाब की सफाई के लिए धन्यवाद। LaTeX से बहुत परिचित नहीं हैं, लेकिन अगली बार एक प्रयास करेंगे।
अलेक्जेंड्रे

आप संपादक बॉक्स के बगल में मार्कडाउन पर मदद पा सकते हैं; LaTeX पर प्राइमर के लिए यहां देखें ।
राफेल

जवाबों:


14

विभिन्न आयाम स्वतंत्र हैं । आप जो कर सकते हैं वह गणना है, प्रत्येक आयाम j के लिए , कितने अलग-अलग चलता है बस उस आयाम में जो कदम उठाते हैं। आइए हम उस नंबर को W ( j , t ) कहते हैं । अपने प्रश्न से, आप पहले से ही जानते हैं कि गतिशील प्रोग्रामिंग के साथ इन नंबरों की गणना कैसे करें।tW(j,t)

अब, यह चलता है कि लेने की संख्या की गणना करने के लिए आसान है आयाम में कदम मैं । आपके पास ( एन) हैtiiआयाम तो interspersing उस आयाम में ले लिया चरणों की कुल संख्या के तरीकेमैंहैटीमैं, और इन तरीकों में से प्रत्येक के लिए आपके पासΠएन1डब्ल्यू(मैं,टीमैं)चलता है। प्राप्त करने के लिए इन पर बीमा राशि Σटी1+टी2+...+टीएन=एम(M(एनटी1,टी2,...,टी)मैंटीमैंΠ1एनडब्ल्यू(मैं,टीमैं) अब, स्मृति नियंत्रण में है, क्योंकि आपको केवलडब्ल्यू(जे,टी)के मूल्यों को याद रखने की आवश्यकता है। समय बड़ेएन केलिए सुपरपोलिमोनियल रूप से बढ़ता है, लेकिन अधिकांश कंप्यूटर में मेमोरी की तुलना में बहुत अधिक समय होता है।

Σटी1+टी2+...+टीएन=(टी1,टी2,...,टीएन) Πमैं=1एनडब्ल्यू(मैं,टीमैं)
डब्ल्यू(जे,टी)एन

आप और भी बेहतर कर सकते हैं। पुनरावर्ती आयामों को दो सबसेट, और B में विभाजित करते हैं , और गणना करते हैं कि कितने चल रहे हैं उपसेट A में केवल आयामों का उपयोग कर रहे हैं , और B के उन लोगों में । इन नंबरों को क्रमशः डब्ल्यू ( टी ) और डब्ल्यू बी ( टी ) कहें। आप के द्वारा चलने की कुल संख्या मिलती हैबीबीडब्ल्यू(टी)डब्ल्यूबी(टी)

Σटी1+टी2=(टी1)डब्ल्यू(टी1)डब्ल्यूबी(टी2)

हाय पीटर। ठीक है, यह गुम अंतर्दृष्टि थी। अब मेरे पास केवल एक शंका बाकी है। बाहरी योग t1, t2 के सभी संभावित संयोजनों पर प्रसारित होता है ... tn to M. sum to M. दुर्भाग्य से, ऐसे संयोजनों की संख्या C (M + 1, N-1) है, जो C (300) जितनी अधिक हो सकती है +1, 10-9)। बहुत बड़ी संख्या ... :(
अलेक्जेंड्रे

1
@Alexandre: मेरा दूसरा एल्गोरिथ्म ("आप और भी बेहतर कर सकते हैं" के साथ शुरू) उस समस्या नहीं है। मैंने अपने उत्तर में पहला एल्गोरिथ्म छोड़ दिया क्योंकि यह पहला ऐसा है जिसके साथ मैं आया था, और क्योंकि मुझे लगता है कि दूसरे एल्गोरिथम को केवल बिना किसी प्रेरणा के देने के बजाय पहले के संस्करण के रूप में समझाना बहुत आसान है।
पीटर शोर

मैंने दूसरा एल्गोरिदम लागू किया। यह तेज़ है, लेकिन अभी भी सबसे बड़ी सीमा के लिए बहुत कम है। पहले एक के साथ समस्या t1, t2, t3, ... के सभी अधिभोगों से अधिक परेशान कर रही थी ... t. (t1), t1 '+ t2' = t1 के समाधानों पर पुनरावृति। और आगे फिर से। आपके द्वारा इंस्टेंट किए गए मामले में यहां कार्यान्वयन है: pastebin.com/e1BLG7Gk । और दूसरे एल्गोरिथ्म में, बहुराष्ट्रीय होना चाहिए एम ओवर टी 1, टी 2 नहीं?
अलेक्जेंड्रे

कोई बात नहीं! उसे हल कर लिया! साथ ही set_ways फ़ंक्शन में संस्मरण का उपयोग करना था। यहाँ अंतिम समाधान है, जो तेजी से धधक रहा है! pastebin.com/GnkjjpBN आपकी अंतर्दृष्टि पीटर के लिए धन्यवाद। आपने दोनों महत्वपूर्ण अवलोकन किए: समस्या स्वतंत्रता और विभाजन और जीत। मैं लोगों को मेरा समाधान देखने की सलाह देता हूं क्योंकि कुछ चीजें हैं जो ऊपर दिए गए उत्तर में नहीं हैं, जैसे कि डब्ल्यू (i, ti) फ़ंक्शन को तीसरे तर्क की आवश्यकता है, जो कि स्थिति है। कि मैं, ती, और स्थिति के संयोजन के मूल्यों के लिए गणना की जानी है। यदि आप कर सकते हैं, तो अपने दूसरे एल्गोरिथ्म में टी 2 मल्टीनोमियल भी जोड़ सकते हैं।
अलेक्जेंड्रे

4

आइए आपके कोड से एक आंतरिक सूत्र के लिए ( s , x 1 , , x n ) एक सूत्र निकालते हैं ( जो कि आंतरिक मामलों को अनदेखा कर रहा है):अभी(रों,एक्स1,...,एक्सn)

अभी(रों,एक्स1,...,एक्सn)=+Σमैं=0nअभी(रों-1,एक्स1,...,एक्समैं-1,एक्समैं+1,एक्समैं+1,...,एक्सn)+Σमैं=0nअभी(रों-1,एक्स1,...,एक्समैं-1,एक्समैं-1,एक्समैं+1,...,एक्सn)

यहाँ कुछ विचार हैं।

  • रों=रों<
  • रोंएक्स1=मैंएक्स1मैं-2एक्स2एक्स1=मैंएक्स1=मैंएक्स2जे-2एक्स2=जे
  • 2एनएक्समैं->0एक्समैं+<डीमैंमैं(2एन)«एन
  • 0«एन2Mx

यह स्मृति उपयोग को काफी कम रखने के लिए पर्याप्त होना चाहिए।


हाय राफेल, मान लीजिए कि हमारा लक्ष्य अब (3, 3, 3, 3) है, ग्रिड 5x5x5 पर। गतिशील प्रोग्रामिंग का उपयोग करना और जैसा कि आपने सुझाव दिया था, हम अब (0, 0, 0, 0) की गणना करेंगे, फिर (0, 0, 0, 1), ... अब (0, 5, 5, 5) का उपयोग करेंगे। किस बिंदु पर हम अब (0, 0, 0, 0) को छोड़ सकते हैं (जो कि (5, 5, 5) से दूर के एक त्रिज्या से अधिक है, क्योंकि अब हमें इसकी गणना करने की आवश्यकता होगी (1, 0, 0, 0) , 0), अब (1, 0, 0, 1), आदि! आपने M << N का एक दो बार उल्लेख किया है, लेकिन सीमाएं 1 <= M <= 300, और 1 <= N <= 10. हैं। चरम सीमा पर, ऐसा नहीं लगता है कि 1 << 300.
अलेक्जेंड्रे

(2,0,0,0)(0,\*,\*,\*)(0,0,0,0)(1,0,0,0)MNMएनM=1N=10

1
1) गोली मैं समझता हूं। यह M * D ^ N से D ^ N तक स्थानिक जटिलता को कम करता है, लेकिन D ^ N अभी भी बहुत अधिक है। मैं यह नहीं देख रहा हूं कि 2) गोली कैसे काम करती है। क्या आप इसका वर्णन करने के लिए मेरी टिप्पणी में उदाहरण का उपयोग कर सकते हैं?
अलेक्जेंड्रे

Dmaxi=1,,NDiDN1DN2i=1NDii=2NDi

यह कैसे करना है, यह नहीं समझा जा सकता है ... आइए मैं समझ गया, और यह कि मैंने स्थानिक जटिलता को कम कर दिया। डी। मूल रूप से, M * D ^ N उपप्रोब्स को अभी भी हल करने की आवश्यकता नहीं है? क्या समस्या को बहुपद बनाने के लिए एक अतिरिक्त संपत्ति की आवश्यकता नहीं है?
अलेक्जेंड्रे
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.