【问题标题】:Sum of different elements in tuples nonexponential algorithm元组非指数算法中不同元素的总和
【发布时间】:2019-06-02 08:46:04
【问题描述】:

我正在做某事,我能够将问题简化为特定形式:给定 k 个整数中的每一个 n 个元组,例如: (a1,a2,a3,a4) , (b1,b2,b3,b4 ) , (c1,c2,c3,c4) , (d1,d2,d3,d4),我希望选择任意数量的元组,当它们相加时,给出一个没有正元素的元组。如果我选择元组 a 和 b,我得到元组 (a1+b1,a2+b2,a3+b3,a4+b4)。因此,如果 a = (1,-2,2,0) 且 b=(-1, 1, -3,0) 则 a+b =(0,-1,-1,0) 不包括正数,因此是问题的解决方案。

除了检查所有子集元组的总和(需要 2^n 步)之外,有没有办法获得解决方案(或验证其不存在)?

由于这个问题出自我的脑海,而不是特定的教科书,我不知道正确的表达方式,并且寻找答案的研究完全是徒劳的。我的大部分搜索都将我引向子集求和问题,我们从列表中选择 k 个元素来求和特定问题。我的问题可以说是一个复杂的问题:我们从一个列表中选择一组元组,我们希望这些元组中每个元素的总和为

编辑:感谢提供的链接,并且由于 cmets 表明小于指数的解决方案很困难,解决了元素范围在 -1,0 和 1 之间的元组的问题对我来说就足够了。此外,元组将有 10,000-20,000 个整数,并且不会超过 1000 个元组。每个元组最多有 10 个 1 和 10 个 -1,其余为零

如果有人也能证明它是某种 NP,那就太好了。

我没有想出DP解决方案,排序似乎没有用

【问题讨论】:

  • 我认为这可能有点抽象/概括/简化为“给出正/负和的整数组合”。对于元组,您可以将其用于元组中的每个位置并获得它们的交集。也许找到一个现有的算法会更容易。
  • 感谢您的想法。假设我希望使用该方法,那么对于每个位置,我需要找到所有有效的位置组合,然后进行它们的交集。我有义务找到每个职位的所有组合,因为省略一个可能会省略一个潜在的解决方案。这意味着我仍然停留在 2^n。
  • 类似于How to find multidimensional path of exact 0 cost with 1, 0, -1 weights。这可能会提示您要搜索哪些关键字。
  • 直觉上,我认为找到一个解决方案确实是 O(2^n),但是如果有一个好的启发式方法,也许你可以在一般情况下做得更好。例如,您可以尝试一些贪心算法:从具有最低(平方?)正数的元组开始(即x^2 for x in t if x > 0maxsum 最小),然后选择下一个元组,使得正数因为组合是最小的,如果在当前路径上找不到解决方案,则重复或回溯。
  • 每个元素的取值范围是多少?

标签: algorithm tuples


【解决方案1】:

这可以在伪多项式时间内使用动态规划在给定的约束条件下求解。

说明

这类似于subset sum problem 的伪多项式时间动态规划解决方案。它仅扩展到多个维度 (4)。

时间复杂度

O(n * sum4) 或者在这种情况下,由于 sum 已由n 界定, O(n5)

解决方案

Demo

这是一个 C++ 中自上而下的动态编程解决方案。

const int N = 50;
int a[50][4]= {{0, 1, -1, 0},
          {1, -1, 0, 0},
          {-1, -1, 0, -1}};

unordered_map<int, bool> dp[N];

bool subset(int n, int sum1, int sum2, int sum3, int sum4)
{
  // Base case: No tuple selected
  if (n == -1  && !sum1 && !sum2 && !sum3 && !sum4)
    return true;
  // Base case: No tuple selected with non-zero sum
  else if(n == -1)
    return false;
  else if(dp[n].find(hashsum(sum1, sum2, sum3, sum4)) != dp[n].end() )
    return dp[n][hashsum(sum1, sum2, sum3, sum4)];

  // Include the current element
  bool include = subset(n - 1,
    sum1 - a[n][0],
    sum2 - a[n][1],
    sum3 - a[n][2],
    sum4 - a[n][3]);
  // Exclude the current element
  bool exclude = subset(n - 1, sum1, sum2, sum3, sum4);

  return dp[n][hashsum(sum1, sum2, sum3, sum4)] = include || exclude;
}

对于memoization,hashsum的计算如下:

int hashsum(int sum1, int sum2, int sum3, int sum4) {
  int offset = N;
  int base = 2 * N;
  int hashSum = 0;
  hashSum += (sum1 + offset) * 1;
  hashSum += (sum2 + offset) * base;
  hashSum += (sum3 + offset) * base * base;
  hashSum += (sum4 + offset) * base * base * base;
  return hashSum;
}

然后驱动程序代码可以搜索任何非正数,如下所示:

int main()
{
  int n = 3;
  bool flag = false;
  int sum1, sum2, sum3, sum4;
  for (sum1 = -n; sum1 <= 0; sum1++) {
    for (sum2 = -n; sum2 <= 0; sum2++) {
      for (sum3 = -n; sum3 <= 0; sum3++) {
        for (sum4 = -n; sum4 <= 0; sum4++) {
          if (subset(n - 1, sum1, sum2, sum3, sum4)) {
            flag = true;
            goto done;
          }
        }
      }
    }
  }
  done:
  if (flag && (sum1 || sum2 || sum3 || sum4))
    cout << "Solution found. " << sum1 << ' ' << sum2 << ' ' << sum3 << ' ' << sum4 << std::endl;
  else
    cout << "No solution found.\n";
  return 0;
}

请注意,总和 (0, 0, 0, 0} 的简单解决方案始终存在,其中从未选择过任何元素,因此在驱动程序代码中被忽略了。

【讨论】:

  • 在我的应用程序中,一个元组中元素的总和确实可以以 5 为界。但是,维度预计为数千(主要是 0)。你的方法仍然有效吗? (如果是这样,我会自己修改它,就在我阅读了一些关于一维子集总和的复习之后
  • @ConfusedSoul 不幸的是,不是。这种方法适用于较小的有界总和,但在维数上呈指数增长。
  • 所以即使有界总和很低,如果维度很高,它仍然是指数的?尽管如此,我还是会做我的研究,并尝试看看我是否可以根据您的工作找到解决方法。 .衷心感谢您提供代码、指导和努力。
  • 作为您添加的限制的必然结果,总和最多为 5,我可以假设元素的数量也以 10 为上限吗?在这种情况下,生成所有子集的指数算法可以正常工作。
  • 每个元组的元素数?不幸的是,预计会有数千人。然而,元组本身的数量可能有数百个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-19
  • 1970-01-01
  • 1970-01-01
  • 2015-09-20
  • 2015-11-01
  • 1970-01-01
相关资源
最近更新 更多