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

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

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

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

2452閲覧

再帰処理からキューやスタックの処理にしたい

ponpo5555

総合スコア9

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2017/07/04 13:58

編集2017/07/06 02:36

今は再帰処理を使って、countで数え上げるプログラムです。
しかし、画像が少し大きくなるとStackOverflowExceptionを起こしてしまいます。
ソースコードは下記のとおりです。

C#

1int Expansion(int y, int x) 2 { 3 if ((x >= width) || (x < 0) || (y >= height) || (y < 0)) return 1; 4 if (x < width && x >= 0 && y < height && y >= 0) 5 { 6 if (img.GetPixel(x, y) == Black) 7 { 8 return 1; 9 } 10 else 11 { 12 img.SetPixel(x, y, Black); 13 return (Expansion(y + 1, x) + Expansion(y - 1, x) + 14 Expansion(y + 1, x + 1) + Expansion(y - 1, x + 1) + 15 Expansion(y + 1, x - 1) + Expansion(y - 1, x - 1) + 16 Expansion(y, x + 1) + Expansion(y, x - 1)); 17 } 18 } 19 else return 1; 20 }

この再帰処理をスタック、もしくはキューで書き換えたいです。
具体的なプログラムがあると、助かります。
何卒お力添えいただければ幸いです。

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

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

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

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

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

guest

回答2

0

ベストアンサー

もともとのプログラムは、いって、もどって、いってを繰り返し、値が求まらない。
いわゆる無限ループになっている。
調べたところは戻らない。一方向にするというようにするために、
たどっていった履歴を残すことで、数え上げる。最後は、たどった履歴を数える。
C#7.0のローカル関数も使っています。
デバッグしていないので、バグあるかも。

//あらかじめ、白黒のデータをDicに突っ込んでおく。GetPixelは確か遅い。 Dictionary<(int, int), bool> imageData = new Dictionary<(int, int), bool>(); public int 連続を求める(int p_x,int p_y) { HashSet<(int, int)> historyList = new HashSet<(int, int)>(); Stack<(int, int)> stack = new Stack<(int, int)>(); IEnumerable<(int,int)> 周辺の座標(int x,int y) { for (int i = -1; i <= 1; i++) { for (int l = -1; l <= 1; l++) { yield return (x + i, y + l); } } } void Check(int x,int y) { if (historyList.Contains((x, y)) == false && imageData.ContainsKey((x, y))) { if (imageData[(x, y)]) { historyList.Add((x, y)); foreach (var item in 周辺の座標( x, y)) { if (historyList.Contains(item) == false) { stack.Push(item); } } } } } //開始地点の入力 stack.Push((p_x, p_y)); while(stack.Count>0) { var (x,y) = stack.Pop(); Check(x,y); } return historyList.Count; }

投稿2017/07/04 15:55

編集2017/07/04 17:57
kiichi54321

総合スコア1984

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

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

ponpo5555

2017/07/05 03:29

「Expansion」のところを「連続を求める」に置き換えるのですかね。 履歴を数えても、画素の連結成分の個数は求まらないような気がするのですが、 動かしてみても意図した答えになりません。
kiichi54321

2017/07/05 05:03

「画素の連結成分の個数」は、同じ色でつながっているところを数え上げるってことじゃないの?
ponpo5555

2017/07/05 05:11

例として、1が白、0が黒 1 1 1 0 0 1 1 0 0 1 1 0 0 0 0 0 0 1 1 0 連結成分の個数3つみたいなことです。
kiichi54321

2017/07/05 05:16

historyList自体が島なんだから、この島が、何個作れるのか?と言うかたちにすればいいという感じですかね。
ponpo5555

2017/07/05 05:27

そうですね...。
ponpo5555

2017/07/05 16:19

無事数え上げることに成功しました!ありがとうございました!
guest

0

Expansionを呼び出し過ぎなので、メモ化、Dictionaryにxy座標に対する結果を格納するとかするといいのでは?
何度も同じ再帰を繰り返してしまうため、計算が無駄です。
動的計画法とかを勉強してみるといいと思います。

Dictonary<(int,int),int> dic = new Dictonary<(int,int),int>() int Expansion(int y, int x) { if ((x >= width) || (x < 0) || (y >= height) || (y < 0)) return 1; if (dic.ContainKey((x,y)) return dic[(x,y)]; if (x < width && x >= 0 && y < height && y >= 0) { var result = 1; if (img.GetPixel(x, y) != Black) { img.SetPixel(x, y, Black); result = (Expansion(y + 1, x) + Expansion(y - 1, x) + Expansion(y + 1, x + 1) + Expansion(y - 1, x + 1) + Expansion(y + 1, x - 1) + Expansion(y - 1, x - 1) + Expansion(y, x + 1) + Expansion(y, x - 1)); } dic.Add((x,y),result); return result; } else return 1; }

これなら座標分の計算しかしないので、スタックとかはとりあえず必要ないんじゃないかなぁ。

投稿2017/07/04 14:23

編集2017/07/04 14:36
kiichi54321

総合スコア1984

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

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

kiichi54321

2017/07/04 14:47

ただ、これって、もともとのプログラムが、ほんとに「連結成分の個数を数える」ことができるのか謎だ。無限ループになっている感じがするし、2重3重に数え上げている感じがする。
ponpo5555

2017/07/04 14:53

回答ありがとうございます。試してみたところ、 Dictionary<(int,int),int> dic = new Dictionary<(int,int),int>(); の部分が扱えませんでした。
kiichi54321

2017/07/04 15:01

System.ValueTuple を nuget する
ponpo5555

2017/07/04 15:52

度々申し訳ないです。 先ほどの部分は無事扱えることができました。 しかし、StackOverflowExceptionをまた起こしてしまいました。
kiichi54321

2017/07/04 15:55

無限ループしているんだよ。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問