【问题标题】:All possible combination. Faster way所有可能的组合。更快的方式
【发布时间】:2011-10-27 20:06:17
【问题描述】:

我有一个 1 到 100 之间的数字向量(这并不重要),它的大小可以在 3 到 1.000.000 之间。

如果有人可以帮助我从该向量中获得 3 个值唯一*组合。

*独特

示例:我在数组中有以下值:1[0] 5[1] 7[2] 8[3] 7[4]([x] 是索引)

在这种情况下 1[0] 5[1] 7[2] 和 1[3] 5[1] 7[4] 是不同的,但是 1[0] 5[1] 7[2] 和 7[ 2] 1[0] 5[1] 相同(重复)

当我处理大量值(例如 1.000.000)时,我的算法有点慢。所以我想要的是一种更快的方法。

           for(unsigned int x = 0;x<vect.size()-2;x++){
                for(unsigned int y = x+1;y<vect.size()-1;y++){
                    for(unsigned int z = y+1;z<vect.size();z++)
                    {

                        // do thing with vect[x],vect[y],vect[z]
                    }
                }
            }

【问题讨论】:

  • 你确定数字在1-100之间的属性不重要吗?
  • 您的目标是获得所有可能的combination 值吗?如果你有 10^9 个值,你最终会得到 10^27 / 3 个组合来测试。多次“思考”可能会很昂贵......
  • 1,000,000 个值有 1,000,000*999,999*999,998/6 个唯一组合。即使您立即获得这些组合中的每一个,光看它们也需要很长时间!
  • 您的示例(不是代码)令人困惑
  • 1[0] 5[1] 7[2] and 1[3] 5[1] 7[4] are diferent 你确定,100% 确定吗?如果是这样,那么您将无法获得更优化(在单个线程中)。

标签: c++ performance algorithm


【解决方案1】:

事实上,您的值在 1 到 100 之间非常重要!因为对于大小为 1,000,000 的向量,您有很多相等的数字,您不需要检查所有数字!您可以执行以下操作:

注意:以下代码只是一个大纲!它可能缺乏足够的错误检查,只是在这里给你一个想法,而不是复制粘贴!

注 2:当我写答案时,我假设数字在 [0, 99] 范围内。然后我读到它们实际上在 [1, 100] 中。显然这不是问题,您可以将所有数字 -1 甚至更好,将所有 100 更改为 101。

bool exists[100] = {0};  // exists[i] means whether i exists in your vector

for (unsigned int i = 0, size = vect.size(); i < size; ++i)
    exists[vect[i]] = true;

然后,你做的和你之前做的类似:

for(unsigned int x = 0; x < 98; x++)
  if (exists[x])
    for(unsigned int y = x+1; y < 99; y++)
      if (exists[y])
        for(unsigned int z = y+1; z < 100; z++)
          if (exists[z])
          {
            // {x, y, z} is an answer
          }

您可以做的另一件事是花更多时间准备,以减少生成配对的时间。例如:

int nums[100];  // from 0 to count are the numbers you have
int count = 0;

for (unsigned int i = 0, size = vect.size(); i < size; ++i)
{
  bool exists = false;
  for (int j = 0; j < count; ++j)
    if (vect[i] == nums[j])
    {
      exists = true;
      break;
    }
  if (!exists)
    nums[count++] = vect[i];
}

然后

for(unsigned int x = 0; x < count-2; x++)
  for(unsigned int y = x+1; y < count-1; y++)
    for(unsigned int z = y+1; z < count; z++)
    {
      // {nums[x], nums[y], nums[z]} is an answer
    }

假设 100 是一个变量,所以我们称它为 k,数组中的实际数字为 m(小于或等于 k)。

使用第一种方法,您有O(n) 准备和O(m^2*k) 操作来搜索值,非常快。

在第二种方法中,您有O(nm) 准备和O(m^3) 用于生成值。考虑到您对nm 的值,准备时间太长了。

实际上,您可以合并这两种方法以获得两全其美的效果,因此如下所示:

int nums[100];           // from 0 to count are the numbers you have
int count = 0;
bool exists[100] = {0};  // exists[i] means whether i exists in your vector

for (unsigned int i = 0, size = vect.size(); i < size; ++i)
{
  if (!exists[vect[i]])
    nums[count++] = vect[i];
  exists[vect[i]] = true;
}

然后:

for(unsigned int x = 0; x < count-2; x++)
  for(unsigned int y = x+1; y < count-1; y++)
    for(unsigned int z = y+1; z < count; z++)
    {
      // {nums[x], nums[y], nums[z]} is an answer
    }

这种方法有O(n) 准备和O(m^3) 成本来找到唯一的三元组。

编辑:原来对于OP来说,不同位置的相同数字被认为是不同的值。如果真的是这样,那么对不起,没有更快的解决方案。原因是所有可能的组合本身都是C(n, m)(即combination),尽管您在O(1) 中生成它们中的每一个,但它对您来说仍然太大了。

【讨论】:

    【解决方案2】:

    真的没有什么可以加快你在那里的循环体的速度。考虑到 1M 向量大小,您正在进行 一万亿 次循环迭代。

    产生所有这样的组合是一个指数问题,这意味着当输入大小变得足够大时,您将无法实际解决它。如果可能,您唯一的选择是利用您的应用程序的特定知识(您需要结果,以及它们将如何使用)来“解决”问题。

    【讨论】:

    • 它将用于检查这 3 个值是否可以构成一个有效的三角形。(一个简单的 if)
    • @Payn3:你需要那个三角形来……?不要在这里回答,你无法在评论中提供足够的信息来进行这种分析。
    • 数值在0到100之间,其实可以提升很多
    • @Payn3: 那么1[0] 5[1] 7[2] and 1[3] 5[1] 7[4] 是否真的不同?你说它们是,但如果你只是检查三角形,那么它们不会有什么不同。
    【解决方案3】:

    您可以对输入进行排序,使其唯一,并在a &lt; b &lt; c 时选择 x[a]、x[b] 和 x[c]。排序将是 O(n log n),选择组合将是 O(n³)。你仍然会有更少的三元组来迭代:

    std::vector<int> x = original_vector;
    std::sort(x.begin(), x.end());
    std::erase(std::unique(x.begin(), x.end()), x.end());
    for(a = 0; a < x.size() - 2; ++a)
      for(b=a+1; b < x.size() - 1; ++b)
         for(c=b+1; c< x.size(); ++c
            issue triplet(x[a],x[b],x[c]);
    

    【讨论】:

      【解决方案4】:

      根据您的实际数据,您可以通过首先创建一个每个值最多包含三个条目的向量并对其进行迭代来显着加快速度。

      【讨论】:

      • 我认为这会占用大量内存,并且生成它的速度与他现在的速度完全一样。
      • 一点也不。我的建议与上面的 shahbaz 基本完全相同。
      【解决方案5】:

      正如 r15habh 所指出的,我认为数组中的值在 1-100 之间这一事实实际上很重要。

      您可以执行以下操作:遍历数组,将值读取到唯一的集合中。这个本身是 O(n) 时间复杂度。该集合将包含不超过 100 个元素,这意味着 O(1) 空间复杂度。

      现在,由于您需要生成所有 3 项排列,因此您仍需要 3 个嵌套循环,但不是对可能巨大的数组进行操作,而是对最多包含 100 个元素的集合进行操作。

      总体时间复杂度取决于您的原始数据集。对于小型数据集,时间复杂度将为 O(n^3)。对于大型数据集,它将接近 O(n)。

      【讨论】:

      • 他说:1[0] 5[1] 7[2] and 1[3] 5[1] 7[4] are diferent 所以你不能删除重复值。
      【解决方案6】:

      如果正确理解您的应用程序,那么您可以改用元组,并根据您的要求存储在集合或哈希表中。如果三元组的法线很重要,那么请确保您移动三元组,这样可以说最大的元素是第一位的,如果法线无关紧要,那么只需对元组进行排序。使用 boost 和整数的版本:

      #include <set>
      #include <algorithm>
      #include "boost/tuple/tuple.hpp"
      #include "boost/tuple/tuple_comparison.hpp"
      
      int main()
      {
          typedef boost::tuple< int, int, int > Tri;
          typedef std::set< Tri > TriSet;
          TriSet storage;
          // 1 duplicate
          int exampleData[4][3] = { { 1, 2, 3 }, { 2, 3, 6 }, { 5, 3, 2 }, { 2, 1, 3 } };
          for( unsigned int i = 0; i < sizeof( exampleData ) / sizeof( exampleData[0] ); ++i )    
          {
              std::sort( exampleData[i], exampleData[i] + ( sizeof( exampleData[i] ) / sizeof( exampleData[i][0] ) ) );
              if( !storage.insert( boost::make_tuple( exampleData[i][0], exampleData[i][1], exampleData[i][2] ) ).second )
                  std::cout << "Duplicate!" << std::endl;
              else
                  std::cout << "Not duplicate!" << std::endl;
          }
      }
      

      【讨论】:

      • 看来我误解了你的问题,呵呵。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      相关资源
      最近更新 更多