Двойные квадраты: подсчет чисел, которые являются суммами двух совершенных квадратов
источник: Кубок Хакеров 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;
вот версия, которая тривиально 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.