3

꼬리 재귀 적 형태로 쓰고 싶은 기능이 있습니다. 이 함수는 s 양면 다이 n 번을 굴려서 k의 합계를 얻는 방법의 수를 계산합니다. this answer에서이 함수의 수학적 솔루션을 보았습니다. 그것은 다음과 같다 :컨볼 루션 (convolution) 기능을 꼬리 재귀 적 형태로 작성할 수 있습니까?

enter image description here

R 나의 참조 재귀 구현이 : 나는이 this answer에서 배운대로 스타일을 통과 계속에서 함수를 다시 쓰기 위해 노력했다

sum_ways <- function(n_times, k_sum, s_side) { 
    if (k_sum < n_times || k_sum > n_times * s_side) { 
    return(0) 
    } else if (n_times == 1) { 
    return(1) 
    } else { 
    sigma_values <- sapply(
     1:s_side, 
     function(j) sum_ways(n_times - 1, k_sum - j, s_side) 
    ) 
    return(sum(sigma_values)) 
    } 
} 

하지만, 나는 성공하지 못했습니다. tail-recursive 형식으로이 함수를 작성하는 방법이 있습니까?

편집

나는 R은 꼬리 재귀 최적화하지 않는다는 것을 알고있다. 내 질문은 R 특정하지 않습니다, 다른 언어로 솔루션을 환영합니다. 꼬리 재귀를 위해 최적화되지 않는 언어 인 경우에도 마찬가지입니다.

+0

봐 :

여기 파이썬에서 계속-통과 스타일로 변환 (하지 적절한 꼬리 전화를 가지고 다른 언어를)입니다. –

+0

@ 42- 이것에 대해 배울 수있어서 기쁩니다. 고맙습니다.하지만 함수의 이름을 변경하는 데 문제가 없으므로 도움이 될 것입니다. – refik

+0

재귀는 매우 효율적인 구현이 아닙니다. 이 경우 동적 프로그래밍/메모를 사용하여 이미 계산 된 'f'값을 저장하고 재사용하십시오. –

답변

2

sapply가 계속-통과 스타일에없는, 당신은 교체해야하므로 그것. ?`Recall`에서

def sum_ways_cps(n_times, k_sum, s_side, ctn): 
    """Compute the number of ways to get the sum k by rolling an s-sided die 
    n times. Then pass the answer to ctn.""" 

    if k_sum < n_times or k_sum > n_times * s_side: 
     return ctn(0) 
    elif n_times == 1: 
     return ctn(1) 
    else: 
     f = lambda j, ctn: sum_ways_cps(n_times - 1, k_sum - j, s_side, ctn) 
     return sum_cps(1, s_side + 1, 0, f, ctn) 

def sum_cps(j, j_max, total_so_far, f, ctn): 
    """Compute the sum of f(x) for x=j to j_max. 
    Then pass the answer to ctn.""" 

    if j > j_max: 
     return ctn(total_so_far) 
    else: 
     return f(j, lambda result: sum_cps(j + 1, j_max, total_so_far + result, f, ctn)) 


sum_ways_cps(2, 7, 6, print) # 6 
+0

와우, 나는 그것을 읽음으로써 stackoverflow를 가졌다. 엄밀히 말하면'sum_ways_cps'는 꼬리 재귀로 간주 될까요? – refik

+0

여기서 재귀는'sum_ways_cps'가'sum_cps'를 꼬리로 호출하고'f'를 꼬리로 호출하여'sum_ways_cps'를 꼬리 호출한다는 것입니다. –

+0

이것은 하나의 함수로 작성할 수 있습니까? –

0

이 시도 (재귀와 함께, 우리는 우리가 꼬리 재귀 버전을 원하는 경우 선형 점화식의 생각해야) :

f <- function(n, k) { 
    if (n == 1) {     # base case 
    return(ifelse(k<=6, 1, 0)) 
    } else if (k > n*6 | k < n) { # some validation 
    return(0) 
    } 
    else { 
    # recursive calls, f(1,j)=1, 1<=j<=6, otherwise 0 
    return(sum(sapply(1:min(k-n+1, 6), function(j) f(n-1,k-j)))) 
    } 
} 

sapply(1:13, function(k) f(2, k)) 
# [1] 0 1 2 3 4 5 6 5 4 3 2 1 0 
+0

이것은 제 구현과 거의 같습니다. 나는 최종 행동이 그 자체에 대한 호출이 아니라 오히려 그 자체에 대한 복수 호출과 합계이기 때문에 그것이 꼬리 재귀 적으로 적합하지 않다고 생각한다. – refik

+0

@refik 원래의 반복 관계가 회선 형태 ('f '의 다중 호출의 합계를 포함한다)이기 때문에 정확한 재귀 적 구현은 같은 유형이 될 것이다. 일부 정수'p, r'과 일부 함수'g'에 대해'f (n, k) = f (np, r) + g (r)'와 같은 반복 관계를 표현할 수 있다면 다음과 같이 쓸 수 있습니다. 꼬리 재귀 함수. –

+0

그 대답을하지 않는다 : "이것을 시도하십시오 ..."보다는 "할 수 없다 ... ..."? – refik