いつも拝見しております。初めて質問させて頂きます。
再帰関数で使用しているクラスですが、処理に時間がかかり、再帰関数の中で従前の
同じクラスの値を複写して使用しているため、コンストラクタ(メンバ変数の初期化)
の処理を省こうかと考えています。このような使用の方法はまずいのでしょうかご教示
願います。
なお 今は開発中ですので、コンソール実行していますが、動作については問題なさそ
うです。
宜しくお願いします。
環境 xppro sp3 vc8 mfc console
クラス及びその使用例は次のとおりです。
////// a.h ////// (クラスの宣言)
class A
{
char M[MAX_SIZE][MAX_SIZE];
//// そのほかのメンバ変数
A();
~A();
init(); //// 従前のコンストラクタ
//// そのほかのメンバ関数
};
////// a.cpp ////// (クラスの利用)
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
A a;
a.init();
///// aに値を代入
foo(&a);
}
void foo(A* a);
{
A* b;
b = new A;
b = *a;
//// 処理
if(条件) {
foo(b);
} else {
return;
}
}
コードから何がしたいのか意図がつかめないんだけど
・ new してるのに delete してないのでリークしてる
・提示コードは new/delete でなくてもいいぢゃん
・コンストラクタでの処理を省略しても結局 operator= で代入してりゃ同じこと
あたりを指摘しとく
> 使用の方法はまずいのでしょうか
コードが断片的過ぎるので判断できない
具体的に何がどうまずいと思っているのかを書いて味噌
まずいのかまずくないのか指摘できるかもしれないし、代案が出るかもしれない
tetrapod様 早速の回答ありがとうございます。
プログラムの内容は、将棋の局面解析(ブロートフォースに近いものを目指してい
ます。)をしており、foo()は、差し手を実行し、その局面の差し手を検索し、foo()
を呼び出すというものです。現段階でfoo()がその局面の手数分、深度としては600
手程再帰呼び出しされますので、変数をヒープ領域に確保しております。
deleteについては転記ミスで returnの前に行っています。
初期化していない変数を条件判断に使用し、苦労したことがありましたので、変数
についてはコード上ではなるべく初期化を行うことを心がけておりますが、この関数
の実行回数が多く実行時間がかかること、呼び出し元のクラスをそのまま代入してい
ることから初期化が不要ではないかと考え質問させて頂きました。
>結局 operator= で代入してりゃ同じこと
ということは、コンストラクタによる初期化は不要と解させて頂いてよろしいのでしょ
うか。
色々突っ込みどころがありますが、まず将棋で600手の読みは
終わらないのではないかと。どの局面も有効な選択肢が何十もあるでしょ。
メンバ変数の初期化については、省いて問題ないと思います。
foo()で必要な変数のみコンストラクタで初期化して、
その他の例えば表示に必要な変数などはinit()で必要に応じて呼び出す。
ただinit()でそれなりに重い処理をしてるんでないならば、
省略は無駄だと思います。ブルートフォースで600手の先読みというのが
天文学的な時間がでも終わらないと思うので。
この辺をどのように見積もってます?
同じ、というのは
コンストラクタでコピーする手間(=処理時間)と
operator= で代入する手間(=処理時間)は同じ
ならば抜けがないようコンストラクタで初期化すべきである
という意図(不要な初期化はいらないけどさ)
再帰を1段抜ける=局面を1手前に戻すということ、と解釈していいかな?
MAX_SIZE が 1000 とかあって大きな領域のコピーが頻発しているのであれば
M は1つだけ用意して、コピーでなく revert するほうが速かったりするかも
とか思ったんだけど、将棋の局面であれば MAX_SIZE はせいぜい9かな?
その程度ならコピー・削除のほうが速そう。
> 現段階でfoo()がその局面の手数分、深度としては600手程
> 再帰呼び出しされますので、
600手先まで読む?ありえない。総合600種類の手を読むならわかるが。
3手先まで読むとしたら再帰深さが3までしかいらないはずだ
今この局面で打てる手が60種類あってもそれは for でまわすだけで済むはず
# 駒は20枚しかないから1局面で着手可能な手は600もないし
評価(局面& 現局面) { // 枝刈りは省略
foreach (現局面.可能な着手) { // ここで再帰する必要は無い
new 新局面(現局面, 着手);
評価(新局面); // 相手手番で評価=先読み深度=再帰深さ
delete 新局面;
}
}
一番遅い場所がどこかは測定済みであって、まちがいなく手番の処理のところである
と判明しているのであればいいけど
プログラムってのはやみくもにいじっても速くならない。
クラスの実装がでかすぎて必要の無いメンバーまで含んでいないか?
必要の無い初期化を行っていないか?
最適化しない状態で走らせていないか?
盤面をコピーコンストラクトする際に無駄な処理をしていないか?
# 駒は20枚しかないから1局面で着手可能な手は600もないし
手持ちの駒は?
たいちう様 tetrapod様 ん?様 回答ありがとうございます。
プログラムは趣味で組んでいますが、相手が大物だけに本腰を据えております。
プログラム自体は8年ほど前に作ってあった(当時はブルートフォースを断念し、
10手先の局面を数量化して評価:一手の応手約1週間)ものですが、パソコンの性能
もよくなり、メモリも格段と増えたため、取り組みを再開したところです。
初手から80手の展開しうる局面をデータベース化(データベースの深度について
も検討中。)し、その局面を解析させています。この深度で1局面あたりの平均の手
数は48手(打駒を含めて)という結果が出ています。(但し相手の歩の頭に駒を打
つような手も1手として考えています。)
そもそもブルートフォースでは、プログラムが収束しないため、その条件(同一局
面が現れたらそれ以降は実行しない等:例えばプログラム上では初手から4手(+2
手)で開始局面に戻る手の組み合わせがいくつもあります。)の検討を重ねている所
です。
特に局面解析の主要な部分は即詰検索となりますが、これが約200手~300手
になります。(局面の詰検索に6時間以上掛かる場合もあり、名人戦並みだなと感心
したりもしておりますが、:私ならあきらめそうな局面でもプログラムは真剣に考え
ます。)
プログラムの最適化を考える中で質問させて頂きましたが、明快な回答をいただき
ありがとうございました。