C++ 一种排序和洗牌等值项的快速算法(最好是通过STL';s)

C++ 一种排序和洗牌等值项的快速算法(最好是通过STL';s),c++,algorithm,sorting,stl,shuffle,C++,Algorithm,Sorting,Stl,Shuffle,我目前正在开发随机优化算法,遇到了以下问题(我想这也会出现在其他地方):它可以称为完全不稳定的部分排序: 给定一个大小为n的容器和一个比较器,使得条目的值相等。 返回最佳k个条目,但如果值相等,则接收其中任何一个条目的可能性(几乎)相等 (输出顺序与我无关,也就是说,最好的k中完全相等的值不需要被洗牌。即使是将all相等的值洗牌也是一个相关的、有趣的问题,就足够了!) 一种非常(!)低效的方法是使用shuffle_random然后partial_sort,但实际上只需要在“选择边界”(即所有等值

我目前正在开发随机优化算法,遇到了以下问题(我想这也会出现在其他地方):它可以称为完全不稳定的部分排序

给定一个大小为n的容器和一个比较器,使得条目的值相等。 返回最佳k个条目,但如果值相等,则接收其中任何一个条目的可能性(几乎)相等

(输出顺序与我无关,也就是说,最好的k中完全相等的值不需要被洗牌。即使是将all相等的值洗牌也是一个相关的、有趣的问题,就足够了!)

一种非常(!)低效的方法是使用
shuffle_random
然后
partial_sort
,但实际上只需要在“选择边界”(即所有等值项块,两者都要快得多)处洗牌等值项块。也许这个观察是从哪里开始的

如果有人能用STL算法(或至少是大部分算法)提供解决方案,我会非常喜欢,因为它们通常都非常快速、封装良好且OMP并行化


Thanx提前为您提供任何想法

首先要进行
部分排序
。然后,当元素不相等时,返回它们。如果遇到一个大于剩余k的相等元素序列,则洗牌并返回第一个k。否则返回全部并继续。

未完全理解您的问题,但如果您是我解决了此问题(如果我阅读正确的话)

由于看起来您无论如何都必须遍历给定的对象,因此您最好为结果构建一个副本,在插入时对其进行排序,并在插入时随机化“相等”项

换句话说,将给定容器中的项目复制到STL列表中,但重载比较运算符以创建B树,如果插入时两个项目相等,则随机选择在当前项目之前或之后插入

通过这种方式,它被最优地遍历(因为它是一棵树),并且每次构建列表时,您都会得到相等的项的随机顺序

它的内存是原来的两倍,但我正在读这篇文章,因为你不想改变原来的列表。如果您不在乎丢失原件,请在插入新列表时从原件中删除每个项目。最糟糕的遍历是您第一次调用函数,因为传入列表可能未排序。但是,由于要用已排序的副本替换列表,因此将来的运行应该会快得多,并且可以通过将根节点指定为length()处的元素来为树选择更好的轴心点


希望这是有帮助的,听起来像一个整洁的项目。:)

如果你真的认为输出顺序是不相关的,那么你需要的是
std::nth_元素
,而不是
std::partial_排序
,因为它通常要快一些。请注意,
std::nth_element
将第n个元素放置在正确的位置,因此您可以执行以下操作,这是100%的标准算法调用(警告:测试不太好;fencepost错误可能性很大):

它首先使用
n_元素
将“最佳”的
k
元素放在
0..k-1
位置,保证第k个元素(或其中一个元素)位于
k-1
位置。然后,它将位置
k-1
之前的元素重新分区,以便相等的元素位于末尾,位置
k-1
之后的元素位于开头。最后,它洗牌相等的元素

        std::sort(validLocations.begin(), validLocations.end(),
        [&](const Point& i_point1, const Point& i_point2)
          {
              if (i_point1.mX == i_point2.mX)
              {
                  return Rand(1.0f) < 0.5;
              }
              else
              {
                  return i_point1.mX < i_point2.mX;
              }
          });
n_元素
O(n)
;两个
分区
操作的总和为
O(n)
;而
random\u shuffle
O(r)
其中
r
是被洗牌的相等元素的数量。我认为所有这些都归结为O(n),因此它具有最佳的可扩展性,但它可能是也可能不是最快的解决方案



注意:您应该使用
std::shuffle
而不是
std::random\u shuffle
,将一个统一的随机数生成器传递给
best\n
。但是我太懒了,没有写出所有的样板文件来做这件事并进行测试。对不起。

如果您不介意对整个列表进行排序,这里有一个简单的答案。将比较器中的结果随机化为等效元素

        std::sort(validLocations.begin(), validLocations.end(),
        [&](const Point& i_point1, const Point& i_point2)
          {
              if (i_point1.mX == i_point2.mX)
              {
                  return Rand(1.0f) < 0.5;
              }
              else
              {
                  return i_point1.mX < i_point2.mX;
              }
          });
排序(validLocations.begin(),validLocations.end(), [&](常数点和i_点1,常数点和i_点2) { if(i_point1.mX==i_point2.mX) { 返回兰特(1.0f)<0.5; } 其他的 { 返回i_point1.mXSTL算法不太可能实现OMP并行化,您通常使用哪种编译器来实现这一点?此外,一般来说,人们的目标是复制行为,这意味着稳定的排序。实际上,STL提供了
std::sort
std::stable\u sort
std::partial\u sort
std::nth\u元素
,但与您(直接)所寻找的内容完全不同。。。您可以选择使用-D_GLIBCXX_PARALLEL进行全局并行化STL(当编译器在此处称为sensful时),或者使用u gnu_PARALLEL::namespace直接调用并行化版本。如果进行部分排序,并且元素相等,不能保证等于容器已排序部分中最后一个元素的元素将位于数组未排序部分的开头。所以你可能会想念他们。(“equal”在这里被解释为25.4(4)个等价类)@rici:没关系,因为您只对需要的前k个元素进行了排序。订单未定义这一事实是一种优势,而不是限制-见鬼,
        std::sort(validLocations.begin(), validLocations.end(),
        [&](const Point& i_point1, const Point& i_point2)
          {
              if (i_point1.mX == i_point2.mX)
              {
                  return Rand(1.0f) < 0.5;
              }
              else
              {
                  return i_point1.mX < i_point2.mX;
              }
          });