■
さて、前回のエントリでは
この部分
template<int RADIX> bool mirror_cmp(int figure, int num) { using namespace pstade::oven; PSTADE_OVEN_FOREACH(i, counting(0,figure/2)) { if( (num / power<RADIX>(figure-i-1)) % RADIX != (num / power<RADIX>(i)) % RADIX ) { return false; } } return true; }
各桁の仮数を取るのに基数のpowerで割っていましたが、毎回のループで
これを計算するのは非効率的な気がします。
これをTable化すれば、もっと効率的かも。。。
というわけでこちらです。
template<int RADIX> class PowTable { public: PowTable(int max_number); int GetPower(int figure) const; private: std::vector<int> table_; }; template<int RADIX> PowTable<RADIX>::PowTable(int max_number) { using namespace pstade::oven; using boost::lambda::_1; counting(0, max_number)|transformed(boost::bind(power<RADIX>,_1))|taken_while(_1 < max_number)|copied_to(std::back_inserter(table_)); } template<int RADIX> int PowTable<RADIX>::GetPower(int figure) const { return table_[figure]; }
桁数figureを渡すと、基数のfigure乗を返してくれます。このクラスも基数に汎用性を持たせて
テンプレートクラスとしました。
そして、これをmirror_cmp内にstaticにおいてもいいですし、引数を追加して、外部から渡してやってもいいようにしてもいいでしょう。引数で渡すときに
template<int RADIX> bool mirror_cmp(int figure, int num, const PowTable<RADIX>& pt);
こんな感じにすれば、基数が違う場合によしなにコンパイルエラーも出してくれるわけです。
今回はstaticに関数内に実装しますか。
template<int RADIX> bool mirror_cmp(int figure, int num) { using namespace pstade::oven; static const PowTable<RADIX> pt(MAX_NUM); PSTADE_OVEN_FOREACH(i, counting(0,figure/2)) { if( (num / pt.GetPower(figure-i-1)) % RADIX != (num / pt.GetPower(i)) % RADIX ) { return false; } } return true; }
MAX_NUM?グローバルにしてしまいましたよ。static const だからいいじゃないですか。
あれ、初期化の順番大丈夫だっけか、あ、同じ翻訳単位中では上からですよね。
結果
debug | 10回実行 | 平均 |
ループごとに計算 | 41.656 | 4.1656 |
テーブル化 | 42.5 | 4.25 |
比率(倍) | 1.02 |
vector::operator[]はデバッグモードだとなかなか遅いことがあるので
release版ではどうでしょうか。
release | 10回実行 | 平均 |
ループごとに計算 | 0.672 | 0.0672 |
テーブル化 | 0.578 | 0.0578 |
比率(倍) | 0.86 |
そもそも、この問題自体は何度も呼び出すものではないので、あんまり意味が無いんですが。。。
劇的に早くなるものでもなかったですし。
一応ソース上げておきます。
visual studio2008SP1
Boost1.42
PStade1.04.3で確認
#include <iostream> #include <pstade/oven.hpp> #include <boost/lambda/lambda.hpp> #include <boost/bind.hpp> #include <iterator> #include <cassert> #include <vector> const int MAX_NUM = 1000000; //1,000,000 template<int RADIX> int power(int N) { if( N > 1 ) { return RADIX * power<RADIX>(N-1); } else if( N == 1 ) { return RADIX; } else if( N == 0 ) { return 1; } else { assert(false); return -1; } } template<int RADIX> class PowTable { public: PowTable(int max_number); int GetPower(int figure) const; private: std::vector<int> table_; }; template<int RADIX> PowTable<RADIX>::PowTable(int max_number) { using namespace pstade::oven; using boost::lambda::_1; counting(0, max_number)|transformed(boost::bind(power<RADIX>,_1))|taken_while(_1 < max_number)|copied_to(std::back_inserter(table_)); } template<int RADIX> int PowTable<RADIX>::GetPower(int figure) const { return table_[figure]; } template<int RADIX> bool mirror_cmp(int figure, int num) { using namespace pstade::oven; static const PowTable<RADIX> pt(MAX_NUM); PSTADE_OVEN_FOREACH(i, counting(0,figure/2)) { if( (num / pt.GetPower(figure-i-1)) % RADIX != (num / pt.GetPower(i)) % RADIX ) { return false; } } return true; } template<int RADIX> int get_figure(int num) { assert(num > 0); int count = 0; while((num = num / RADIX)) { ++count; } return ++count; } int test1() { using namespace pstade::oven; int sum = 0; PSTADE_OVEN_FOREACH(n, counting(1, MAX_NUM)) { if( mirror_cmp<10>( get_figure<10>(n), n ) && mirror_cmp<2>( get_figure<2>(n), n ) ) { sum += n; } } std::cout << sum << std::endl; return 0; }