많은 수의 하위 문제가있는 동적 프로그래밍


11

많은 수의 하위 문제가있는 동적 프로그래밍. 그래서 인터뷰 거리 에서이 문제를 해결하려고합니다.

그리드 워킹 (50 점 점수)
당신은에 위치하고 있습니다 N 위치에 차원 그리드 (x1,x2,,xN) . 격자의 치수는 (D1,D2,,DN )입니다. 한 단계에서 N 차원 중 하나에서 한 단계 앞뒤로 걸을 수 있습니다 . (그래서 항상 거기에 2N 가능한 다른 이동). 얼마나 많은 방법으로 M 을 취할 수 있습니까M어떤 시점에서 그리드를 떠나지 않도록 단계? xi , xi0 또는 경우 그리드를 떠납니다 xi>Di.

내 첫 번째 시도는이 메모 된 재귀 솔루션이었습니다.

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_pointn 튜플이며, 여기서 n10 입니다. 그래서 사실, 기능 수 number_of_ways(steps, x1, x2, x3, ... x10)와 .1xi100

교과서에서 본 동적 프로그래밍 문제에는 거의 모두 twp 변수가 있으므로 2 차원 행렬 만 필요합니다. 이 경우 10 차원 매트릭스가 필요합니다. 그래서 총 세포.10010

mnmin(m,n)

최신 정보

Peter Shor의 제안을 사용하고, 약간의 수정, 특히 함수 에서 위치를 추적 하고 치수를 두 세트 A와 B로만 분할하는 것이 아니라 재귀 적으로 효과적으로 분할을 수행 해야 할 필요성 한 세트의 차원 만있는 기본 사례에 도달 할 때까지 분할 및 정복 방법.W(i,ti)

다음 구현을 생각해 냈습니다.이 테스트는 최대 실행 시간 미만의 모든 테스트를 통과했습니다.

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
"다수의 단계 및 / 또는 차원에서 실패"-여기서 "실패"는 무엇을 의미합니까?
Raphael

1
어서 오십시오! 나는 a) 적절한 Markdown 및 LaTeX 형식을 사용하고 (나중에 스스로에게 부탁하십시오) b) 불필요한 거터를 제거하기 위해 귀하의 질문을 편집했습니다. 우리는 C 코드의 덩어리를 신경 쓰지 않습니다. 아이디어 , 즉 중심 사물의 의사 코드로 자신을 제한하십시오 .
Raphael

실패는 mem[]사전 을 채워 사용 가능한 모든 시스템 메모리를 소진 함을 의미합니다 . 내 답변을 정리해 주셔서 감사합니다. LaTeX에 익숙하지 않지만 다음에 노력할 것입니다.
Alexandre

편집기 상자 옆의 Markdown에 대한 도움말을 찾을 수 있습니다. LaTeX의 입문서는 여기 를 참조 하십시오 .
Raphael

답변:


14

다른 차원은 독립적 입니다. 당신이 할 수있는 일은 각 차원 j 에 대해 단계를 거치는 차원에 몇 개의 다른 보행이 있는지 계산 하는 것 입니다. 그 번호를 라고합시다 . 귀하의 질문에서, 당신은 이미 동적 프로그래밍으로 이러한 숫자를 계산하는 방법을 알고 있습니다.W ( J , t )tW(j,t)

이제 차원 에서 단계를 수행 하는 보행 수를 쉽게 계산할 수 있습니다 . 당신은 너무 차원에서 촬영 단계의 총 수 있다는 차원을 산재하는 방법 입니다 , 이러한 각각의 방법에 대해, 당신은 산책을. 이것을 합산하여 이제 값만 기억하면되므로 메모리가 제어됩니다 . 큰 의 경우 시간이 엄청나게 커지지 만 대부분의 컴퓨터는 메모리보다 많은 시간이 있습니다. I ( N을tiiitiΠ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

더 잘할 수 있습니다. 재귀 개의 서브 세트들로 나누는 차원 및 , 및 계산이 얼마나 많은 서브 세트에 바로 사용 치수가 걸어 , 단지 이들에 . 이 숫자들을 각각 및 . 당신은 총 산책 횟수를 얻습니다.AA B W A ( t ) W B ( t )BABWA(t)WB(t)

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

안녕 피터. 알았어요. 통찰력이 없었습니다. 이제 한가지 의심의 여지가 남아 있습니다. 외부 합계는 M에 해당하는 t1, t2, ... tn의 모든 가능한 조합을 반복합니다. 불행히도, 이러한 조합의 수는 C (M + 1, N-1)이며 C (300)만큼 높을 수 있습니다 +1, 10-9). 매우 큰 숫자 : :(
Alexandre

1
@Alexandre : 두 번째 알고리즘 ( "더 나은 작업을 시작할 수 있습니다"로 시작)에는 그런 문제가 없습니다. 첫 번째 알고리즘은 내가 만든 첫 번째 알고리즘이기 때문에 첫 번째 알고리즘을 남겼으며 두 번째 알고리즘을 동기 부여없이 제공하는 것보다 첫 번째 알고리즘의 변형으로 설명하는 것이 훨씬 쉽다고 생각합니다.
Peter Shor

두 번째 알고리즘을 구현했습니다. 더 빠르지 만 가장 큰 범위에는 너무 낮습니다. 첫 번째 문제는 M에 합한 t1, t2, t3, ... tn의 모든 가능성을 반복하는 것이 었습니다. 두 번째 알고리즘은 t1 + t2 = M에 대한 솔루션을 반복합니다. 그러나 Wa에 대해서도 동일하게 수행해야합니다. (t1), t1 '+ t2'= t1에 대한 솔루션을 반복합니다. 그리고 재귀 적으로. pastebin.com/e1BLG7Gk 인 경우 구현이 있습니다 . 그리고 두 번째 알고리즘에는 t1, t2에 대해 M이 다항식이어야합니다.
Alexandre

신경 쓰지 마! 해결했습니다! set_ways 함수에서도 메모를 사용해야했습니다. 최종 솔루션은 다음과 같습니다. pastebin.com/GnkjjpBN 통찰력 Peter에게 감사합니다. 문제 독립성과 분할 및 정복이라는 두 가지 주요 관찰을 수행했습니다. 위의 답변에없는 몇 가지 사항이 있기 때문에 사람들이 내 솔루션을 살펴 보는 것이 좋습니다. 예를 들어 위치 인 세 번째 인수가 필요한 W (i, ti) 기능 i, ti 및 위치의 값 조합에 대해 계산해야합니다. 가능하면 두 번째 알고리즘에 다항식 t2를 추가하십시오.
Alexandre

4

코드에서 대한 공식을 추출해 봅시다 내부 셀의 경우 경계 케이스를 무시 함).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)

몇 가지 아이디어가 있습니다.

  • 대한 모든 값을 계산 한 후에는 대한 모든 계산 된 값을 삭제할 수 있습니다 .s=ks<k
  • 고정 된 , 테이블 항목을 사전 식 순서로 계산해야합니다 (단순하기 때문에). 그런 다음, 모든 셀은 "1의 반경"안에 그러한 셀만 필요합니다. 즉, 좌표가 1보다 더 멀리있을 수는 없습니다. 따라서 반복이 하면 대한 모든 값을 삭제할 수 있습니다 . 충분하지 않으면 대해 동일한 작업을 수행하십시오 -고정 경우 에 도달 하면 및 값을 삭제하십시오 .x 1 = i x 1i 2 x 2 x 1 = i x 1 = i x 2j 2 x 2 = jsx1=ix1i2x2x1=ix1=ix2j2x2=j
  • "가장 가능한 다른 이동 이 항상 있습니다"는 그리드의 중간에만 유지됩니다 . , 모든 대해 및 입니다 . 그러나 그것은 또한 중간에 대답이 쉽다는 것을 의미합니다 : 그것은 단지 입니다. 작동하는 동적 프로그래밍 반복이있는 경우이 테이블만으로도 대부분의 테이블을 면도 할 수 있습니다 ( ).X I - M > 0 X I + M < D I I ( 2 N ) M M « N2NxiM>0xi+M<Dii(2N)MMN
  • 주목해야 할 또 다른 사항은 전체 테이블을 계산할 필요가 없다는 것입니다. 어쨌든 대부분의 값은 으로 채워집니다 ( ). 주위 의 가장자리 길이 의 (하이퍼) 큐브로 자신을 제한 할 수 있습니다 (그리드를 떠나는 경로 때문에 찌그러짐에 유의하십시오).M N 2 M x0MN2Mx

메모리 사용량을 매우 낮게 유지하기에 충분해야합니다.


안녕하세요 라파엘, 우리의 목표는 그리드 5x5x5에서 (3, 3, 3, 3)이라고 가정 해 봅시다. 동적 프로그래밍을 사용하고 제안한대로 lex 순서를 사용하면 now (0, 0, 0, 0), (0, 0, 0, 1), ... now (0, 5, 5, 5)를 계산합니다. 어떤 시점에서 지금 (0, 0, 0, 0) ((5, 5, 5)에서 1 이상의 반경을 버릴 수 있습니다. 지금 계산하기 위해 필요하기 때문에 (1, 0, 0) , 0), now (1, 0, 0, 1) 등? M << N을 두 번 언급했지만 그 범위는 1 <= M <= 300, 1 <= N <= 10입니다. , 극단적으로, 그것은 1 << 300처럼 보이지 않습니다.
Alexandre

1) 두 번째 글 머리표에서 불분명 한 것은 무엇입니까? 을 계산하자마자 버릴 수 있습니다 . 그러나 버릴 수있는 가장 빠른 지점은 아닙니다 . 마지막으로 필요한 셀은 입니다. 2) 솔직히 과 의 특정 값에 대해 너무 걱정하지 않습니다 . 차라리 일반적인 문제를보고 싶습니다. 당신이없는 경우 , 마지막 두 개의 총알이 훨씬 도움이되지 않습니다. 이고 이면 효과를 알기에 충분해야하며 어느 전략도 아프지 않습니다. ( 0 , \ * , \ * , \ * ) ( 0 , 0 , 0 , 0 ) ( 1 , 0 , 0 , 0 ) M N M N M = 1 N = 10(2,0,0,0)(0,\*,\*,\*)(0,0,0,0)(1,0,0,0)MNMNM=1N=10
Raphael

1
1) 내가 이해하는 총알. 이는 공간 복잡성을 M * D ^ N에서 D ^ N으로 줄이지 만 D ^ N은 여전히 ​​너무 많습니다. 그래도 2) 글 머리 기호가 어떻게 작동하는지 잘 모르겠습니다. 내 의견에 예제를 사용하여 설명 할 수 있습니까?
Alexandre

@Alexandre 나는 이전 의견에서했었다. I 읽을 경우 의미하는 것으로 에 공간 복잡도를 감소하면 다음 두 번째 탄환을 적용 , 두 번째 시간 및 곧. (보다 정확하게는 에서 등으로 이동합니다.)최대 = 1 , ... , N D I D N - 1 D N - 2 Π N = 1 D를 I Π N은 내가 = 2 D의 난을Dmaxi=1,,NDiDN1DN2i=1NDii=2NDi
Raphael

어떻게해야할지 모르겠다 ... 내가 이해하고 공간 복잡성을 D로 줄 였다고 가정 해 봅시다. 근본적으로, M * D ^ N 하위 문제를 여전히 해결하지 않아도 될까요? 문제를 다항식으로 만들기 위해 추가 속성이 필요하지 않습니까?
Alexandre
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.