Programação dinâmica com grande número de subproblemas. Então, eu estou tentando resolver esse problema da Interview Street:
Grid Walking (Score 50 points)
Você está situado em uma grade dimensional na posição . As dimensões da grade são ). Em uma etapa, você pode dar um passo à frente ou atrás em qualquer uma das dimensões. (Portanto, sempre existem movimentos diferentes possíveis). De quantas maneiras você pode tomar etapas de modo que você não saia da grade em nenhum momento? Você sai da grade se, para qualquer , ou .
Minha primeira tentativa foi esta solução recursiva memorizada:
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 surpresa: falha em um grande número de etapas e / ou dimensões devido à falta de memória.
Portanto, o próximo passo é melhorar minha solução usando programação dinâmica. Mas antes de começar, estou vendo um grande problema com a abordagem. O argumento starting_point
é um duplo, onde é tão grande quanto . Portanto, de fato, a função poderia estar number_of_ways(steps, x1, x2, x3, ... x10)
com .
Os problemas de programação dinâmica que eu já vi nos livros didáticos quase todos têm variáveis twp, de modo que apenas uma matriz bidimensional é necessária. Nesse caso, seria necessária uma matriz de dez dimensões. Então, células no total.
ATUALIZAR
Usando as sugestões de Peter Shor e fazendo algumas correções menores, notavelmente a necessidade de acompanhar a posição na função e, em vez de apenas dividir as dimensões em dois conjuntos A e B, fazendo a divisão recursivamente, efetivamente usando um método de dividir e conquistar, até que um caso base seja alcançado, onde apenas uma dimensão está no conjunto.
Eu vim com a seguinte implementação, que passou em todos os testes abaixo do tempo máximo de execução:
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
fonte
mem[]
dicionário. E obrigado por limpar minha resposta. Não está muito familiarizado com o LaTeX, mas fará um esforço na próxima vez.Respostas:
As diferentes dimensões são independentes . O que você pode fazer é calcular, para cada dimensão j , quantas caminhadas diferentes existem naquela dimensão que executa etapas. Vamos chamar esse número de W ( j , t ) . Com a sua pergunta, você já sabe como calcular esses números com programação dinâmica.t W( j , t )
Agora, é fácil de contar o número de passeios que levam passos na dimensão i . Você tem maneiras de intercalar dimensões, de modo que o número total de etapas executadas na dimensão seja e, para cada uma dessas maneiras, você ande. Soma-os para obter Agora, a memória está sob controle, pois você só precisa se lembrar dos valores . O tempo cresce superpolinomialmente para grande , mas a maioria dos computadores tem muito mais tempo que memória.tEu Eu itiΠN1W(i,Ti)Σt1+t2+...+tN=M(H( Nt1, t2, … , TM) Eu tEu ΠN1W( i , tEu) W(j,t)N
Você pode fazer ainda melhor. Recursivamente dividir as dimensões em dois subgrupos, e , e calcular o número de passeios não são usando apenas as dimensões em subconjunto , e apenas aqueles em . Ligue para esses números e , respectivamente. Você obtém o número total de caminhadas porB A B W A ( t ) W B ( t )UMA B UMA B WUMA( T ) WB( T )
fonte
Vamos extrair uma fórmula para do seu código (para uma célula interna, que está ignorando casos de borda):agora( s ,x1, ... ,Xn)
Aqui estão algumas idéias.
Isso deve ser suficiente para manter o uso de memória bastante baixo.
fonte