【问题标题】:Minimally partitioning a vector of objects (C++)最小划分对象向量 (C++)
【发布时间】:2015-03-06 17:22:17
【问题描述】:

我有一个 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} ]

这样在每个分区内,所有条目都属于不同的组。

【问题讨论】:

  • 也许一个例子可以帮助说明你的意思。我不确定我是否理解您的问题。
  • 那么你想取一个Objs的向量,并输出Objs的多个向量,其中每个向量只包含Objs与Obj::group的值相同?
  • 恰恰相反......我想输出许多 Obj 的向量(尽可能少),其中每个向量包含属于 DIFFERENT 组的 Obj。
  • @markt1964:为什么?你发现了什么算法?我们不是你的研究助理!

标签: c++ partitioning


【解决方案1】:

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

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

  • 初始化一个空直方图H
  • 对于 v 中的每个项目 x
    • H[x] 增加 1,如果没有这样的 bin 存在,则在之前将其初始化为零
  • mH 中的最大频率
  • 初始化空向量v1, ..., vm
  • 对于每个值 x 分别为 H[x] ≥ 0:
    • 对于 i ← 1 到 H[x]:
      • x 附加到 vi

请注意,如果向量中的对象具有确定它们是否与它们的 only 数据成员相等的键,则此方法可以正常工作。但是,如果它们有更多需要保留但不参与确定相等性的状态,则可以轻松调整过程以解决这一问题。

  • 初始化一个空直方图H
  • 对于 v 中的每个项目 x
    • H[key(x)] 加一,如果不存在这样的 bin,则在之前将其初始化为零
  • mH 中的最大频率
  • 初始化空向量v1, ..., vm
  • 对于 v 中的每个值 x
    • iH[key(x)]
    • x 附加到 vi
    • H[key(x)] 减一

如果您想要一个快速的解决方案,您可以使用std::unordered_map<int, int> 作为您的直方图。

以下是 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";
    }
}

如果给定一些众所周知的字符串作为输入,它会产生以下输出。

input:  WEWEREARRESTEDAFTERDADATEDEEREGGS
output: 10 partitions

[W, F, A, T, D, R, E, G, S]
[W, S, T, R, A, D, E, G]
[R, T, A, D, E]
[A, R, D, E]
[R, E]
[E]
[E]
[E]
[E]
[E]


input:  ALASDADHADAGLASSSALAD
output: 8 partitions

[H, G, S, L, A, D]
[D, L, S, A]
[L, D, A, S]
[S, D, A]
[A]
[A]
[A]
[A]


input:  THEQUICKBROWNFOXJUMPSOVERTHESLEAZYDOG
output: 4 partitions

[Q, I, C, K, B, W, N, F, X, J, U, M, P, V, R, T, H, S, L, E, A, Z, Y, D, O, G]
[T, H, U, R, S, O, E]
[O, E]
[E, O]

【讨论】:

    【解决方案2】:

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

    std::vector<std::vector<Obj>> partitions;
    sort(yourVector);
    for (each group of equal Obj) {
        if(sizeOfThisGroup > partitions.size())
            add enough partitions
        split the group into the partitions
    }
    

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

    【讨论】:

    • 假设 group1 递增
    • @LightnessRacesinOrbit groups 可以是任意的ints(甚至是任意、可排序的键类型的任意值)。我不明白为什么需要上述内容。
    • 如果我们不接受这个假设,那么您创建的分区太多了。您是否要为一个输入创建 25 亿个 Objgroup 2500000000
    • @LightnessRacesinOrbit 也许我对算法的描述不清楚,但它works 并且很容易实现。如果确实不清楚,请告诉我问题所在或澄清自己。
    • @LightnessRacesinOrbit 啊,你认为thisGroup 应该是关键吗?我可以看到出错了。澄清。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-05
    • 1970-01-01
    • 2011-10-01
    • 2011-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多