Двойные квадраты: подсчет чисел, которые являются суммами двух совершенных квадратов

источник: Кубок Хакеров Facebook Квалификационный Раунд

двойной квадрат числа-это число 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

7 ответов


Факторизуйте число n и проверьте, имеет ли оно простой фактор p с нечетной оценкой, такой, что p = 3 (mod 4). Это происходит тогда и только тогда, когда n не является суммой двух квадратов.

число решений имеет выражение замкнутой формы, включающее число делителей n. См.эта теорема 3 для точного утверждения.


вот мой простой ответ в O(sqrt(n)) сложности

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

x должно быть целое число so (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

вот гораздо более простое решение:

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)

цикл через все пары (a, b) неосуществим, учитывая ограничения на X. существует более быстрый способ!

для фиксированного 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;

см. его на ideone с вводом образца


вот версия, которая тривиально O(sqrt (N)) и избегает всех внутренних ветвей цикла.

начните с генерации всех квадратов до предела, легко сделать без каких-либо умножений, а затем инициализировать индекс l и R.

на каждой итерации вы вычисляете сумму, затем обновляете два индекса и количество на основе сравнения с целевым значением. Это итерации sqrt(N) для генерации таблицы и максимальных итераций sqrt(N) цикла поиска. Предполагаемый ход время с разумным компилятором составляет максимум 10 тактов на sqrt( N), поэтому для максимального входного значения, если 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).


Я спешил, поэтому решил его, используя довольно грубый подход (очень похожий на marcog), используя Python 2.6.

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 будет странно, если число является полным квадратом.


количество решений (x,y) из

x^2+y^2=n

над целыми числами ровно в 4 раза больше числа делителей n, конгруэнтных 1 mod 4. Аналогичные идентичности существуют также для проблем

x^2 + 2y^2 = n

и

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