【发布时间】:2011-08-06 16:19:20
【问题描述】:
我有一个函数,它接收一个字符列表并生成下一个字典排列。为了好玩,我尝试泛化代码以使用迭代器,以及能够生成更多不同类型的排列。
template<typename ITER>
bool nextPermutation(ITER start, ITER end, std::random_access_iterator_tag)
{
for(ITER i = end-1; i != start; --i)
{
if(*(i-1) < *i)
{
// found where can be swapped
for(ITER j = end-1; j != (i-1); --j)
{
if(*(i-1) < *j)
{
// found what to swap with
auto temp = *j;
*j = *(i-1);
*(i-1) = temp;
// put everything from i on into "sorted" order by reversing
for(ITER k = end-1; k > i; --k,++i)
{
temp = *k;
*k = *i;
*i = temp;
}
return true;
}
}
}
}
return false;
}
但是,我遇到了一些问题,即当我不使用原始指针时,代码的性能会明显变慢。这是我的测试台:
template<typename ITER>
bool nextPermutation(ITER start, ITER end, std::random_access_iterator_tag);
template<typename ITER>
bool nextPermutation(ITER start, ITER end)
{
return nextPermutation(start, end, std::iterator_traits<ITER>::iterator_category());
}
#define USE_VECTOR
int main(void)
{
bool hasNext = true;
#ifdef USE_VECTOR
std::vector<char> c;
for(char i = '0'; i <= '9'; ++i)
{
c.push_back(i);
}
for(size_t i = 0; i < 999999 && hasNext; ++i)
{
hasNext = nextPermutation(c.begin(), c.end());
}
#else
char c[] = "0123456789";
size_t LENGTH = 10;
for(size_t i = 0; i < 999999 && hasNext; ++i)
{
hasNext = nextPermutation(c, c+LENGTH);
}
#endif
std::cout << "done" << std::endl;
std::cin.ignore();
return 0;
}
定义 USE_VECTOR 后,运行此测试台大约需要 20 秒。当我取消定义它时,代码在不到一秒的时间内运行(我没有写任何计时代码,但足以说明性能上有非常显着的差异)。
现在我的问题是,我在哪里会受到如此巨大的性能影响,这会影响使用迭代器(std::string 迭代器、std::vector 迭代器等)与原始指针的对比?
【问题讨论】:
-
嗯,你开启优化了吗?另外,为什么不使用
std::swap之类的东西? -
主要是懒惰。我最初使用
char*编写代码,当我回来时,我只是修改了函数定义以便能够使用迭代器。编辑:哦,我忘记了 Visual Studio 默认情况下不优化调试版本。杜尔:( -
好吧,不提
std::next_permutation就太残忍了……阅读标准库的实现,即使你想自己动手,也是很有教育意义的。 -
是的,但我是从学习的角度写这篇文章的(另外,我是那些发现算法令人着迷的人之一)。我仍然对使用指针有点不舒服,当标准库中的东西混入时更是如此,所以我认为这将是一个很好的练习。
-
如果您喜欢处理排列和组合的算法,这里对您来说更有趣:home.roadrunner.com/~hinnant/combinations.html 只阅读规范,而不是源代码,然后尝试实现它。但是源代码也在那里。