Programmation dynamique avec un grand nombre de sous-problèmes. J'essaie donc de résoudre ce problème depuis Interview Street:
Grid Walking (Score 50 points)
Vous êtes situé dans une grille à dimensions à la position . Les dimensions de la grille sont ). En une seule étape, vous pouvez avancer ou reculer d'une étape dans l'une des dimensions. (Il y a donc toujours mouvements différents possibles). De combien de façons pouvez-vous prendre des étapes telles que vous ne quittez la grille à aucun moment? Vous quittez la grille si pour tout , soit ou .
Mon premier essai a été cette solution récursive mémorisée:
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
Grande surprise: elle échoue pour un grand nombre d'étapes et / ou de dimensions par manque de mémoire.
La prochaine étape consiste donc à améliorer ma solution en utilisant la programmation dynamique. Mais avant de commencer, je vois un problème majeur avec l'approche. L'argument starting_point
est un tuple, où est aussi grand que . Donc, en fait, la fonction pourrait être number_of_ways(steps, x1, x2, x3, ... x10)
avec .
Les problèmes de programmation dynamique que j'ai vus dans les manuels ont presque tous des variables twp, de sorte que seule une matrice à deux dimensions est nécessaire. Dans ce cas, une matrice à dix dimensions serait nécessaire. Soit cellules au total.
Avec les matrices 2D en programmation dynamique, seule la ligne de calculs précédente est généralement nécessaire pour le calcul suivant, réduisant ainsi la complexité spatiale de à min ( m , n ) . Je ne sais pas comment je ferais la même chose dans ce cas. La visualisation d'une table n'est pas possible, donc la réponse devrait provenir directement de la récursion ci-dessus.
MISE À JOUR
En utilisant les suggestions de Peter Shor et en apportant quelques corrections mineures, notamment la nécessité de garder une trace de la position dans la fonction , et plutôt que de diviser uniquement les dimensions en deux ensembles A et B, en procédant au fractionnement récursivement, en utilisant efficacement une méthode de division et de conquête, jusqu'à ce qu'un scénario de base soit atteint où une seule dimension est dans l'ensemble.
J'ai trouvé l'implémentation suivante, qui a réussi tous les tests en dessous du temps d'exécution maximal:
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
la source
mem[]
dictionnaire. Et merci d'avoir nettoyé ma réponse. Pas trop familier avec LaTeX mais fera un effort la prochaine fois.Réponses:
Les différentes dimensions sont indépendantes . Ce que vous pouvez faire est de calculer, pour chaque dimension j , combien de parcours différents il y a dans cette seule dimension qui prennent étapes. Appelons ce nombre W ( j , t ) . D'après votre question, vous savez déjà comment calculer ces nombres avec une programmation dynamique.t W(j,t)
Maintenant, il est facile de compter le nombre de marches qui font pas dans la dimension i . Vous avez ( Nti i façons d'interpénétrer les dimensions de sorte que le nombre total de pas effectués dans la dimensionisoitti, et pour chacune de ces façons, vous avezwalksN1W(i,ti) desmarches. Additionnez-les pour obtenir
∑t1+t2+…+tN=M(M(Nt1,t2,…,tM) i ti ΠN1W(i,ti)
Maintenant, la mémoire est sous contrôle, car il suffit de se souvenir des valeursW(j,t). Le temps augmente de façon superpolynomiale pour les grandsN, mais la plupart des ordinateurs ont beaucoup plus de temps que la mémoire.
la source
Voici quelques idées.
Cela devrait être suffisant pour maintenir une utilisation de la mémoire assez faible.
la source