【问题标题】:How to optimize a for loop?如何优化for循环?
【发布时间】:2011-06-15 17:14:00
【问题描述】:

我有一个问题,让我们说: 找到所有两对数字 (x,y) 和 (z,t) 使得 x³ + y³ = z³ + t³,其中 (x, y) != (z, t) 和 x³ + y³ 。

以 10,000 的立方根 21.544 -> 向下取整到 21,所以我得到:

#include <iostream>

using namespace std;

int main() {
    for( int x = 1; x <= 20; ++x ) {
        for( int y = x + 1; y <= 21; ++y ) {
            for( int z = x + 1; z <= y - 1; ++z ) {
                for( int t = z; t <= y - 1; ++t ) {
                    if( x*x*x + y*y*y == z*z*z + t*t*t ) {
                        cout << x << ", " << y << ", " << z << ", " << t << endl; 
                    }
                }
            } 
        }
    }
    return 0;
}

我知道这段代码可以进一步优化,而这正是我所寻找的。另外,我的一位朋友告诉我 y 可能是 x + 2 而不是 x + 1,我对此表示怀疑,因为 if
x = 1,那么我们将永远不会有 y = 2,在这种情况下,它错过了一个可能的解决方案。

有什么想法吗?

【问题讨论】:

标签: c++ algorithm numbers logic


【解决方案1】:

在当前循环结构的情况下,可以进行一项明显的算法优化,您可以通过将范围限制为 10,000 的立方根来进行非常正确的优化。但是,您可以更进一步,根据 10,000 - x 的立方根来限制 y 的范围。这是你可以做的一件事。

另一个优化是地球上没有理由应该是 4 个循环。只需执行 2 个循环并计算 x^3 + y^3 的值并检查重复项。 (这与您在不深入研究立方根的特征的情况下获得的一样好。) 这实际上并没有正确使用 API,但您明白了:

multimap<int, std::pair<int, int> > map;
for (int i = 1; i < 21; i++) {
    (for int j = x; j < cube_root(10000 - i^3); j++ {
        multimap.insert (i^3 + j^3, std::pair<int, int>(i,j);

然后您只需遍历多图并寻找重复。

【讨论】:

  • +1 六个答案,这是唯一一个提到减少循环计数的答案(尽管没有太多细节)。这些人在想什么?
  • @Nikita 让我们了解详情。
  • +1 用于制作立方体总和表:O(N^2/3 log N):尽管我们仍然可以稍微改进它。我只是先生成立方体表,知道何时传递 N,然后生成多哈希图(比多图复杂度低)。
  • 感谢您提供优雅的解决方案!
【解决方案2】:

典型的权衡:内存换速度。

首先x 的界限非常大:如果我们假设(x,y) 与x &lt;= y 排序,那么

    x^3 + y^3 < N and x^3 < y^3 (for positive numbers)
=>  x^3 + x^3 < N (by transitivity)
<=> x^3 < N/2
<=> x <= floor((N/2)^(1/3))

因此x &lt;= 17在这里。

现在,让我们记住x^3 + y^3 的结果并构建一个关联表(sum -> pairs)。顺便说一句,有理由将(x,x) 作为一对丢弃吗?

int main(int argc, char* argv[])
{
  typedef std::pair<unsigned short, unsigned short> Pair;
  typedef std::vector<Pair> PairsList;
  typedef std::unordered_map<unsigned short, PairsList> SumMap;

  // Note: arbitrary limitation, N cannot exceed 2^16 on most architectures
  // because of the choice of using an `unsigned short`
  unsigned short N = 10000;
  if (argc > 1) { N = boost::lexical_cast<unsigned short>(argv[1]);  }

  SumMap sumMap;

  for (unsigned short x = 1; x*x*x <= N/2; ++x)
  {
    for (unsigned short y = x; x*x*x + y*y*y <= N; ++y)
    {
      sumMap[x*x*x + y*y*y].push_back(Pair(x,y));
    }
  }

  for (SumMap::const_reference ref: sumMap)
  {
    if (ref.second.size() > 1)
    {
      std::cout << "Sum: " << ref.first
                << " can be achieved with " << ref.second << "\n";
      // I'll let you overload the print operator for a vector of pairs
    }
  }

  return 0;
}

我们在这里是 O(N^2)。

【讨论】:

  • 感谢您提供非常详细的分析和出色的解决方案;)
【解决方案3】:

列出所有数字及其运算结果。按结果对列表进行排序。测试具有不同操作数的匹配结果。

【讨论】:

    【解决方案4】:

    使用从总和到生成该总和的数字对集合的表格。

    您可以通过两个嵌套的 for 循环生成该表,然后运行该表以收集多个解决方案的总和。

    【讨论】:

      【解决方案5】:

      我建议在外部循环中计算幂(编辑:将计算移出for 循环):

      int x3, y3, z3;
      for( int x = 1; x <= 20; ++x ) {
          x3 = x * x * x;
          for( int y = x + 1; y <= 21; ++y ) {
              y3 = y * y * y;
              for( int z = x + 1; z <= y - 1; ++z ) {
                  z3 = z * z * z;
                  for( int t = z; t <= y - 1; ++t ) {
                      if( x3 + y3 == z3 + t*t*t ) {
                          cout << x << ", " << y << ", " << z << ", " << t << endl; 
                      }
                  }
              } 
          }
      }
      

      无论如何,你为什么要优化(至少对于这个例子)?这在我的 PC 上运行 20 毫秒...所以我猜你在更大范围内也有类似的问题。

      【讨论】:

      • @shnaader:这不仅仅是关于编程,而是关于逻辑思维。我只是想试试,仅此而已!无论如何,谢谢!
      • 您有正确的想法,但在编程上弄错了:x3、y3 和 z3 直到它“继续”即下一个循环迭代时才计算出来,所以第一次这些都是未初始化的,随后它们发生冲突与 ++x 的序列点,因此无法确定此时 x 是什么。
      • 虽然 Etienne 是 xxx、xxx_plus_yyy 和 zzz 的 missing 声明,但我发现这种风格比在循环增量表达式中死记硬背更具可读性——而且它在第一次迭代时就可以正常工作。
      • @CashCow:谢谢,我首先使用了正确的版本,但认为将计算移动到for 中会更优雅。事实证明,它更优雅,但错误......(正如 Fred 所说,你也可以争论可读性)
      【解决方案6】:

      作为一般总结:

      • 在循环时而不是在结束时计算立方体,因此 int xcubed = x*x*x; 就在 x 的循环之后(与 y 和 z 类似)。这样可以节省您多次计算相同的值。将它们放在一个表格中,这样您只需计算一次。
      • 使用某种程度的 hash_table 创建一个立方体总和表,并让它保存重复项(不要与散列冲突混淆)。
      • 任何有重复的都是解决方案。

      顺便说一句,1729 应该是您的解决方案之一。 1 立方加 12 立方,还有 9 立方 + 10 立方。

      要测试性能,您当然可以选择更高的 maxsum 值(并运行多次)。

      算法严格来说是 O(N^2/3)。 (2/3 因为你只去 N 的立方根,然后在那个较小的范围内它是 O(m^2))。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-11-03
        • 2010-11-15
        • 2011-08-30
        • 1970-01-01
        • 2015-04-15
        • 1970-01-01
        • 1970-01-01
        • 2020-11-07
        相关资源
        最近更新 更多