C++ 对象向量的最小划分(C+;+;)

C++ 对象向量的最小划分(C+;+;),c++,partitioning,C++,Partitioning,我有一个对象的std::vector,其中向量中的每个元素或多或少看起来像: struct Obj { int group; }; 向量中的条目没有特定的顺序。通常,在分区时,人们可能希望通常将具有共同点的元素分组到同一分区中,在我的例子中,我想要的实际上是重新排列这个向量中的条目,并以这样的方式对它进行分区,即使用可能的绝对最小分区数,其中单个分区中的每个元素与同一分区中的每个元素属于不同的组 这是否可以在不迭代向量的每个置换并查看每个置换有多少分区的情况下实现 编辑: 要求提供一个示例

我有一个对象的
std::vector
,其中向量中的每个元素或多或少看起来像:

struct Obj {
  int group;
};
向量中的条目没有特定的顺序。通常,在分区时,人们可能希望通常将具有共同点的元素分组到同一分区中,在我的例子中,我想要的实际上是重新排列这个向量中的条目,并以这样的方式对它进行分区,即使用可能的绝对最小分区数,其中单个分区中的每个元素与同一分区中的每个元素属于不同的组

这是否可以在不迭代向量的每个置换并查看每个置换有多少分区的情况下实现

编辑:

要求提供一个示例,因此我将尝试提供一个

如果对象的初始向量为

[ {1}, {2}, {3}, {2}, {3}, {1}, {4}, {5}, {3}, {2} ]
最佳分区是将其划分为三个分区,如下所示:

[ {1}, {2}, {3}, {4}, {5} ] [ {1}, {2}, {3} ] [{2}, {3} ]

因此,在每个分区中,所有条目都属于不同的组。

最简单的方法可能是使用以下算法(伪代码):

std::向量分区;
排序(向量);
对于(每组相等的Obj){
if(sizeOfThisGroup>partitions.size())
添加足够的分区
将组拆分为分区
}

这在
O(nlog(n))
中运行。如果至多
m Obj
是相等的,那么您将得到正好
m
分区。这显然是最低限度的

最简单的方法可能是以下算法(伪代码):

std::向量分区;
排序(向量);
对于(每组相等的Obj){
if(sizeOfThisGroup>partitions.size())
添加足够的分区
将组拆分为分区
}

这在
O(nlog(n))
中运行。如果至多
m Obj
是相等的,那么您将得到正好
m
分区。这显然是最低限度的

如果我正确理解了您的需求,那么“最小分区数”由原始向量中单个值的最大频率给出。因此,您可以创建一个直方图,然后在其中找到最大条目。(向量的大小是线性的。)现在创建m个向量(其中m是刚刚确定的最大频率),并将m个相同的值分配给其中一个。可以保证以分区中不会出现重复的方式分发其余元素

在大小为n的输入向量v的伪代码中:

  • 初始化空直方图H
  • 对于v中的每个项目x:
    • 增量H [ x ]  如果没有这样的箱子已经存在,则在初始化之前将其归零
  • m← 最大频率(单位:H)
  • 初始化空向量v1,…,vm
  • 对于每个值x为H [ x ]  ≥0:
    • 因为我← 1至H [ x ] :
      • 在vi后面加上x
请注意,如果向量中的对象具有确定它们是否作为其唯一数据成员相等的键,则此操作非常有效。但是,如果它们具有更多需要保留但不参与确定相等的状态,则可以轻松调整此过程以考虑此问题

  • 初始化空直方图H
  • 对于v中的每个项目x:
    • 增量H [ 键(x) ]  如果没有这样的箱子已经存在,则在初始化之前将其归零
  • m← 最大频率(单位:H)
  • 初始化空向量v1,…,vm
  • 对于v中的每个值x:
    • 我← H [ 键(x) ] 
    • 在vi后面加上x
    • 减量H [ 键(x) ]  一个
如果你想要一个快速的解决方案,你可以为你的直方图使用a

下面是C++14中的一个(最终有点过于泛化)实现的样子

#include <algorithm>            // std::max_element
#include <functional>           // std::hash, std::equal_to
#include <iterator>             // std::iterator_traits
#include <unordered_map>        // std::unordered_map
#include <vector>               // std::vector

template<typename FwdIterT,
         typename ValueT = typename std::iterator_traits<FwdIterT>::value_type,
         typename ValueHashT = std::hash<ValueT>,
         typename ValueEqCmpT = std::equal_to<ValueT>>
decltype(auto)
min_partition(const FwdIterT begin, const FwdIterT end)
{
  std::vector<std::vector<ValueT>> partitions {};
  std::unordered_map<ValueT, int, ValueHashT, ValueEqCmpT> histo {};
  for (auto iter = begin; iter != end; ++iter)
    histo[*iter]++;
  const auto cmpfreq = [](const auto& lhs, const auto& rhs){
    return lhs.second < rhs.second;
  };
  const auto maxbin = std::max_element(histo.cbegin(), histo.cend(), cmpfreq);
  partitions.resize(maxbin->second);
  for (auto iter = begin; iter != end; ++iter)
    partitions.at(histo.at(*iter)-- - 1).push_back(*iter);
  return partitions;
}

如果我正确理解您的需求,那么“最小分区数”是由原始向量中单个值的最大频率给出的。因此,您可以创建一个直方图,然后在其中找到最大条目。(这是向量大小的线性关系。)现在创建m个向量(其中m是刚刚确定的最大频率)并将m个相同的值中的每一个分配给其中一个。可以保证您可以以一种不会在分区中出现重复的方式分配其余元素

在大小为n的输入向量v的伪代码中:

  • 初始化空直方图H
  • 对于v中的每个项目x:
    • 增量H [ x ]  如果没有这样的箱子已经存在,则在初始化之前将其归零
  • m← 最大频率(单位:H)
  • 初始化空向量v1,…,vm
  • 对于每个值x为H [ x ]  &geq;0:
    • 因为我← 1至H [ x ] :
      • 在vi后面加上x
请注意,如果向量中的对象具有确定它们是否作为其唯一数据成员相等的键,则此操作非常有效。但是,如果它们具有更多需要保留但不参与确定相等的状态,则可以轻松调整此过程以考虑此问题

  • 初始化空直方图H
  • 对于v中的每个项目x:
    • 增量H [ 键(x) ]  如果没有这样的箱子已经存在,则在初始化之前将其归零
  • m← 最大频率(单位:H)
  • 初始化空向量v1,…,vm
  • 对于v中的每个值x:
    • 我← H [ 键(x) ] 
    • 在vi后面加上x
    • 减量H [ 键(x) ]  一个
如果你想要一个快速的解决方案,你可以为你的直方图使用a

下面是C++14中的一个(最终有点过于泛化)实现的样子

#include <algorithm>            // std::max_element
#include <functional>           // std::hash, std::equal_to
#include <iterator>             // std::iterator_traits
#include <unordered_map>        // std::unordered_map
#include <vector>               // std::vector

template<typename FwdIterT,
         typename ValueT = typename std::iterator_traits<FwdIterT>::value_type,
         typename ValueHashT = std::hash<ValueT>,
         typename ValueEqCmpT = std::equal_to<ValueT>>
decltype(auto)
min_partition(const FwdIterT begin, const FwdIterT end)
{
  std::vector<std::vector<ValueT>> partitions {};
  std::unordered_map<ValueT, int, ValueHashT, ValueEqCmpT> histo {};
  for (auto iter = begin; iter != end; ++iter)
    histo[*iter]++;
  const auto cmpfreq = [](const auto& lhs, const auto& rhs){
    return lhs.second < rhs.second;
  };
  const auto maxbin = std::max_element(histo.cbegin(), histo.cend(), cmpfreq);
  partitions.resize(maxbin->second);
  for (auto iter = begin; iter != end; ++iter)
    partitions.at(histo.at(*iter)-- - 1).push_back(*iter);
  return partitions;
}

也许举个例子可以说明你的意思。我不确定我是否理解你的问题
#include <iostream>             // std::cout
#include <string>               // std::string
#include <utility>              // std::begin, std::end

int
main(int argc, char * * argv)
{
  using std::begin;
  using std::end;
  for (int i = 1; i < argc; ++i)
    {
      const std::string text {argv[i]};
      const auto partitions = min_partition(begin(text), end(text));
      std::cout << "input:  " << text << "\n";
      std::cout << "output: " << partitions.size() << " partitions\n\n";
      for (auto it1 = begin(partitions); it1 != end(partitions); ++it1)
        {
          std::cout << "[";
          for (auto it2 = begin(*it1); it2 != end(*it1); ++it2)
            std::cout << (it2 != begin(*it1) ? ", " : "") << *it2;
          std::cout << "]\n";
        }
      if (i != argc - 1)
        std::cout << "\n\n";
    }
}