3

私はテールの再帰的な形で書く機能を持っています。この関数は、の合計を得る方法の数を、sの両面ダイn回を丸めて計算します。私はthis answerでこの関数の数学的解を見てきました。私は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)) 
    } 
} 

、しかし:Rで

enter image description here

マイ参照再帰的な実装は次のようになります。それは以下の通りです。私は成功しなかった。 tail-recursive形式でこの関数を書く方法はありますか?

EDIT

私はRは末尾再帰のために最適化しないことを知っています。私の質問はR固有ではなく、他の言語のソリューションも同様に歓迎されます。たとえそれが末尾再帰を最適化しない言語であったとしても。

+0

ルック:

は、ここではPythonで継続渡しスタイルへの変換(ないが適切な末尾再帰を持っている別の言語)です。 –

+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

関連する問題