質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

再帰

情報工学における再帰とは、プログラムのあるメソッドの処理上で自身のメソッドが再び呼び出されている処理の事をいいます。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

2回答

4675閲覧

メモ化を使った再帰による編集距離の導出を簡単にしたいです。

Hinomae

総合スコア7

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

再帰

情報工学における再帰とは、プログラムのあるメソッドの処理上で自身のメソッドが再び呼び出されている処理の事をいいます。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2020/07/04 05:01

編集2020/07/04 05:33

長さがlen_x、len_yの二つの文字列の編集距離(レーベンシュタイン距離)を、二次元配列memo[len_x+1][len_y+1]に1度計算済みの編集距離を保存することで計算量をおさえつつ求めたいです。
同じ計算が必要になったときにこの保存してあった編集距離の値を参照する…というプログラムです。

最小で以下の再帰関数ldmemoの呼び出し回数をlen_x*len_yに抑えられることは分かっているのですが、実装の仕方が分かりません。

以下のメモ化の場合、例えば2文字と2文字の文字列の場合、再帰関数ldmemoの呼び出し回数は13回、3文字と3文字の場合28回、4文字と4文字の場合49回となります。これをそれぞれ22=4回、33=9回、4*4=16回…となるように実装したいです。

C

1#include <stdio.h> 2#include <stdlib.h> 3 4int countm=0; //再帰関数ldmemoの呼び出し回数 5 6int ldmemo(char *X,int m,char *Y,int n,int **memo); 7 8int main(void) 9{ 10 char *data_x; 11 char *data_y; 12 int len_x,len_y; 13 int **memo; 14 //memo:(len_x+1)*(len_y+1)要素数の二次元配列(標準入力からの読み取りは省略しています) 15 16 for(int i=0; i<len_x+1; ++i){ //memoを全て-1に初期化 17 for(int j=0; j<len_y+1, ++j){ 18 memo[i][j]=-1; 19 } 20 } 21 22 printf("%d ",ldmemo(data_x,len_x,data_y,len_y,memo)); //編集距離を出力 23 printf("%d\n",countm); //再帰関数の呼び出し回数を出力 24} 25 26int delta(char *X,int a,char *Y,int b) //X[a]とY[b]の合否によって0or1を返す 27{ 28 if(X[a]==Y[b]){ 29 return 0; 30 }else{ 31 return 1; 32 } 33} 34 35int ldmemo(char *X,int m,char *Y,int n,int **memo) //改良したい再帰関数 36{ 37 int edit=0; //返り値(編集距離) 38 39 if(memo[m][n]!=-1){ //求める編集距離が既にメモされている場合それを採用 40 edit_d=memo[m][n]; 41 }else if(m>=0 && n>=0){ //そうでない場合再帰によって編集距離edit_dを求めつつメモに記録 42 if(m==0 || n==0){ 43 edit_d=vmax_(m,n); //mとnの最大値を返す 44 memo[m][n]=edit_d; 45 }else{ 46 memo[m-1][n-1]=ldmemo(X,m-1,Y,n-1,memo); 47 ++countm; 48 49 memo[m-1][n]=ldmemo(X,m-1,Y,n,memo); 50 ++countm; 51 52 memo[m][n-1]=ldmemo(X,m,Y,n-1,memo); 53 ++countm; 54 55 edit_d=vmin3_(memo[m-1][n-1]+delta(X,m-1,Y,n-1),memo[m-1][n]+1,memo[m][n-1]+1); 56 //vmin3_:3つの整数の最小値を返す 57 } 58 } 59 memo[m][n]=edit_d; 60 return edit_d; 61}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

呼び出し回数をカウントする位置が悪いです
最初の呼び出しとメモ化した計算結果を返すだけの呼び出しとを区別せずカウントしてます
関数が呼び出されたとき、メモ化されておらず計算が必要な場合に1度だけカウントするように変えれば期待した動作になるでしょう。

投稿2020/07/04 07:20

yudedako67

総合スコア2047

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

計算量がO(MN)となることと、この問題においての再帰回数が最大MN回に収まることは等価ではありません。
レーベンシュタイン問題の計算量がO(MN)となるアルゴリズムは以下の記事を参照してください。
疑似コードに関して最も理解しやすいのはwikipediaの記事。
レーベンシュタイン距離 | wikipedia
一般式の考え化がわかりやすいのはこのqiitaの記事。
典型的な DP (動的計画法) のパターンを整理 Part 1 ~ ナップサック DP 編 ~

2重ループの中身(本処理)の回数が高々MN回となるからO(MN)と表します。
上記の記事では再帰を使っていません。

投稿2020/07/04 06:14

hope_mucci

総合スコア4447

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問