【问题标题】:Double Squares: counting numbers which are sums of two perfect squares双平方:计算两个完全平方和的数字
【发布时间】:2011-06-07 02:47:02
【问题描述】:

来源:Facebook Hacker CupQualification Round 2011

双平方数是一个整数 X,可以表示为两个完全平方的和。例如,10 是一个双正方形,因为 10 = 32 + 12。给定 X,我们如何确定可以将其写为两个平方和的方式数?比如10只能写成32 + 12(我们不算12 + 32 不同)。另一方面,25 可以写成 52 + 02 或 42 + 32 .

你需要解决这个问题,因为 0 ≤ X ≤ 2,147,483,647。

例子:

  • 10 => 1
  • 25 => 2
  • 3 => 0
  • 0 => 1
  • 1 => 1

【问题讨论】:

  • 请注意,这一轮现已结束。
  • 没有像codejam那样普及。刚知道。
  • @Senthil 可能是件好事,因为平台遇到了许多问题。

标签: algorithm math


【解决方案1】:

这是一个简单的 O(sqrt(N)) 版本,并且避免了所有循环内部分支。

首先生成所有达到极限的正方形,无需任何乘法即可轻松完成,然后初始化 l 和 r 索引。

在每次迭代中计算总和,然后根据与目标值的比较更新两个索引和计数。这是生成表的 sqrt(N) 次迭代和搜索循环的最大 sqrt(N) 次迭代。使用合理编译器的估计运行时间是每个 sqrt(N) 最多 10 个时钟周期,因此对于最大输入值,如果 2^31 (sqrt(N) ~= 46341) 这应该对应于少于 500K 时钟周期或十分之几一秒:

unsigned countPairs(unsigned n)
{
    unsigned sq = 0, i;
    unsigned square[65536];

    for (i = 0; sq <= n; i++) {
        square[i] = sq;
        sq += i+i+1;
    }

    unsigned l = 0, r = i-1, count = 0;

    do {
        unsigned sum = square[l] + square[r];
        l += sum <= n;       // Increment l if the sum is <= N
        count += sum == n;   // Increment the count if a match
        r -= sum >= n;       // Decrement r if the sum is >= N
    } while (l <= r);
    return count;
}

一个好的编译器可以注意到最后的三个比较都使用相同的操作数,所以它只需要一个 CMP 操作码,然后是三个不同的条件移动操作 (CMOVcc)。

【讨论】:

    【解决方案2】:

    解的个数(x,y)

    x^2+y^2=n

    整数上的整数正好是 n 的除数的 4 倍,等于 1 mod 4。 问题也存在类似的身份

    x^2 + 2y^2 = n

    x^2 + y^2 + z^2 + w^2 = n.

    【讨论】:

    • -1 表示不关心进行适当的研究。有这么简单的公式,但和你描述的不一样。有关详细信息,请参阅mathworld.wolfram.com/SumofSquaresFunction.html
    • 根据您的说法,n=9 将有 8 个解,因为它有两个除数 d,d mod 4 = 1(即 d=1 和 d=9)。然而,n=9 根本没有解!
    • @vog 9 = 3^2 + 0^2 与问题陈述中的25 = 5^2 + 0^2 相同。不为答案辩护,只是吹毛求疵。
    【解决方案3】:

    这是我在O(sqrt(n))complexity 中的简单回答

    x^2 + y^2 = n
    x^2 = n-y^2 
    x = sqrt(n - y^2)
    

    x 应该是整数,所以 (n-y^2) 应该是完美的正方形。循环到y=[0, sqrt(n)] 并检查(n-y^2) 是否是完美正方形

    伪代码

    count = 0;
    for y in range(0, sqrt(n))
        if( isPerfectSquare(n - y^2))
             count++
    return count/2
    

    【讨论】:

      【解决方案4】:

      我很着急,所以使用 Python 2.6 使用相当蛮力的方法(非常类似于 marcog 的方法)解决了它。

      def is_perfect_square(x):
          rt = int(math.sqrt(x))
          return rt*rt == x
      
      def double_sqaures(n):
          rng = int(math.sqrt(n))
          ways = 0
          for i in xrange(rng+1):
              if is_perfect_square(n - i*i):
                  ways +=1
          if ways % 2 == 0:
              ways = ways // 2
          else:
              ways = ways // 2 + 1
          return ways
      

      注意:当数字是完美平方时,ways 将是奇数。

      【讨论】:

        【解决方案5】:

        分解数 n,并检查它是否有一个具有奇数估值的素数因子 p,使得 p = 3 (mod 4)。当且仅当 n 不是两个平方和时才会这样做。

        解的数量有一个包含 n 的除数的封闭形式表达式。请参阅this, Theorem 3 以获得准确的声明。

        【讨论】:

        • IIRC,这是最好的解决方案。 (同样的问题也出现在 SPOJ 或其他地方。)对于因式分解,它还有助于使用筛子预先计算出 √MAX 的素数列表。)
        • 这个算法不如简单的蛮力算法快,因为蛮力算法是O(√n),但是用筛子找到低于√n的素数是O(√n log log (n) ),如果我们假设这种情况下的 log log n 很小,那么它仍然是 O(√n),并且单个操作的数量也比简单的大。
        • @Saeed:你只计算质数一次。然后是因式分解(这确实是 O(sqrt(n)) 最坏的情况,但平均而言要好得多)。无论哪种方式,您都需要蛮力。
        • 好的,所以你说找到低于 2,147,483,647 的素数并将其保存在数组中,大约有 99940774 个素数低于 int 限制 (n/ln(n)),这将占用大量内存,低于 √n 的素数仍然是 √n/ln(√n),大约是 √n,所以如果你的基本计算量大于简单的蛮力算法基本计算量(ln(n) 次),你的运行时间仍然比简单的慢。如果您想简单地比较它,请编写代码,与即 marcog 简单算法进行比较。
        • @Saeed:你必须计算最高 46340 的素数。如果你愿意,你可以手动完成。
        【解决方案6】:

        这是一个更简单的解决方案:

        create list of squares in the given range (that's 46340 values for the example given)
        
        for each square value x
          if list contains a value y such that x + y = target value (i.e. does [target - x] exist in list)
            output √x, √y as solution (roots can be stored in a std::map lookup created in the first step)
        

        【讨论】:

          【解决方案7】:

          考虑到 X 的约束,遍历所有对 (a, b) 是不可行的。不过有一种更快的方法!

          对于固定的 a,我们可以计算出 b:b = √(X - a2)。 b 并不总是整数,所以我们必须检查一下。由于精度问题,请以较小的容差进行检查:如果 b 是 x.99999,我们可以相当肯定它是一个整数。因此,我们遍历 a 的所有可能值并计算 b 是整数的所有情况。我们需要注意不要重复计算,所以我们设置了 a 2 + b2,在此约束下,a 最多为 √(X/2)。

          这是该算法在 C++ 中的实现:

          int count = 0;
          // add EPS to avoid flooring x.99999 to x
          for (int a = 0; a <= sqrt(X/2) + EPS; a++) {
              int b2 = X - a*a; // b^2
              int b = (int) (sqrt(b2) + EPS);
              if (abs(b - sqrt(b2)) < EPS) // check b is an integer
                  count++;
          }
          cout << count << endl;
          

          See it on ideone with sample input

          【讨论】:

          • For X = a2 + b2, a and b will each be at most √(X/2) errr... 什么?这不是直接与您在问题中给自己举的例子相矛盾吗?
          猜你喜欢
          • 2012-10-24
          • 2018-12-07
          • 2023-03-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-07-23
          • 2017-04-16
          • 1970-01-01
          相关资源
          最近更新 更多