【问题标题】:Improving performance when randomizing a std::list随机化 std::list 时提高性能
【发布时间】:2012-04-28 20:45:03
【问题描述】:

我有一个 std::list,我目前正在使用 Fisher-Yates shuffle 随机化它(请参阅 http://en.wikipedia.org/wiki/Fisher-Yates_shuffle)。总而言之,我的代码在列表中执行了以下步骤:

  1. 循环遍历list 的每个元素。
  2. 从当前位置开始用随机选择的元素交换元素,包括它自己。

因为列表不提供随机访问,这意味着我在第 1 步中迭代整个列表,并且对于每个元素我再次迭代,从那时起平均超过一半的剩余元素。这是我的程序性能的一个主要瓶颈,所以我正在寻求改进它。由于其他原因,我需要继续使用list 作为我的容器,但我正在考虑在我的随机函数开始时转换为vector,然后在最后转换回list。我的列表通常包含 300 到 400 个项目,所以我猜想容器之间的转换成本是值得的,以避免按顺序遍历这些项目。

我的问题是:这似乎是优化代码的最佳方式吗?有没有更好的办法?

【问题讨论】:

  • 显示代码本身比描述代码的作用更有帮助。
  • 与 std::vectors 相比,交换 std::list 元素的成本很高。在进行交换之前尝试将列表复制到向量(简单),看看它是否会有所改善。
  • 也许双端队列是一种选择?它支持列表和向量语义,因此您可以使用 STL 中的 random_shuffle

标签: c++ performance list random std


【解决方案1】:

一个简单的改进是将数据复制到向量中,将向量打乱,然后将其复制回列表中。这就是 Max 和 PeskyGnat 在 cmets 中的建议:

vector<int> myVector(myList.size());
copy(myList.begin(), myList.end(), myVector.begin());
random_shuffle(myVector.begin(), myVector.end());
list<int> myListShuffled(myVector.begin(), myVector.end());

这个实现非常快。但是,它会对向量执行 3 次传递,您可以通过自己实现 shuffle 将其减少到 2 次传递:

vector<int> myVector(myList.size());
int lastPos = 0;
for(list<int>::iterator it = myList.begin(); it != myList.end(); it++, lastPos++) {
    int insertPos = rand() % (lastPos + 1);
    if (insertPos < lastPos) {
        myVector[lastPos] = myVector[insertPos]; 
    }

    myVector[insertPos] = *it;
}

list<int> myListShuffled(myVector.begin(), myVector.end());

由于第一个版本更易于理解且不易出错,因此几乎总是更可取...除非这段代码可能对您的性能至关重要(并且您通过测量确认了这一点。)

编辑:顺便说一下,由于您正在查看 Wikipedia 文章,因此第二个代码示例使用了 Fisher-Yates 的“由内而外”变体。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-24
    • 2020-03-12
    • 2020-02-20
    • 1970-01-01
    相关资源
    最近更新 更多