Несмотря на обоснованные причины для свойств, существует общая идея объектно-ориентированного дизайна, заключающаяся в том, что разоблачение состояния участника через свойства является плохим дизайном. Статья Роберта Мартина об Открытом Закрытом Принципе расширяет это, заявляя, что Свойства поощряют связь и поэтому ограничивают возможность закрыть класс от модификации - если вы измените свойство, всем потребителям класса понадобится также измениться. Он квалифицирует, что подвергая переменные-члены не обязательно плохой дизайн, это может быть просто плохой стиль. Однако, если свойства доступны только для чтения, вероятность злоупотреблений и побочных эффектов меньше.
Лучший подход, который я могу предложить для модульного тестирования (и это может показаться странным), - это сделать как можно больше свойств защищенный или внутренний. Это предотвратит связь, не поощряя писать глупые тесты для геттеров и сеттеров.
Есть очевидные причины, по которым должны использоваться свойства чтения / записи, такие как свойства ViewModel, которые привязаны к полям ввода и т. Д.
Более практично, модульные тесты должны обеспечивать функциональность с помощью общедоступных методов. Если код, который вы тестируете, использует эти свойства, вы получаете бесплатное покрытие кода. Если окажется, что эти свойства никогда не будут подсвечиваться с помощью покрытия кода, существует очень сильная возможность:
Если вы пишете тесты для геттеров и сеттеров, вы получаете ложное ощущение охвата и не сможете определить, действительно ли свойства используются функциональным поведением.
Я нашел метод, который работает примерно на 35% быстрее, чем ваш код 6bit + Carmack + sqrt, по крайней мере, с моим процессором (x86) и языком программирования (C / C ++). Ваши результаты могут отличаться, особенно потому, что я не знаю, как будет действовать фактор Java.
У меня тройной подход:
int64 x
.) if( x < 0 || (x&2) || ((x & 7) == 5) || ((x & 11) == 8) )
return false;
if( x == 0 )
return true;
int64 y = x;
y = (y & 4294967295LL) + (y >> 32);
y = (y & 65535) + (y >> 16);
y = (y & 255) + ((y >> 8) & 255) + (y >> 16);
// At this point, y is between 0 and 511. More code can reduce it farther.
Чтобы фактически проверить, является ли остаток квадратом, я ищу ответ в предварительно вычисленном Таблица. if( bad255[y] )
return false;
// However, I just use a table of size 512
if((x & 4294967295LL) == 0)
x >>= 32;
if((x & 65535) == 0)
x >>= 16;
if((x & 255) == 0)
x >>= 8;
if((x & 15) == 0)
x >>= 4;
if((x & 3) == 0)
x >>= 2;
На этом этапе, чтобы наше число было квадратом, оно должно быть 1 mod 8. if((x & 7) != 1)
return false;
Основная структура леммы Хензеля следующая. (Примечание: непроверенный код; если он не работает, попробуйте t = 2 или 8.) int64 t = 4, r = 1;
t <<= 1; r += ((x - r * r) & t) >> 1;
t <<= 1; r += ((x - r * r) & t) >> 1;
t <<= 1; r += ((x - r * r) & t) >> 1;
// Repeat until t is 2^33 or so. Use a loop if you want.
Идея состоит в том, что на каждой итерации вы добавляете один бит в r, «текущий» квадратный корень из x; каждый квадратный корень является точным по модулю все большей и большей степени 2, а именно t / 2. В конце r и t / 2-r будут квадратными корнями из x по модулю t / 2. (Обратите внимание, что если r является квадратным корнем из x, то и -r. Это верно даже по модулю чисел, но будьте осторожны, по модулю некоторых чисел вещи могут иметь даже более 2 квадратных корней; в частности, это включает степени 2. ) Поскольку наш фактический квадратный корень меньше 2 ^ 32, в этот момент мы можем просто проверить, являются ли r или t / 2-r действительными квадратными корнями. В моем реальном коде я использую следующий модифицированный цикл: int64 r, t, z;
r = start[(x >> 3) & 1023];
do {
z = x - r * r;
if( z == 0 )
return true;
if( z < 0 )
return false;
t = z & (-z);
r += (z & t) >> 1;
if( r > (t >> 1) )
r = t - r;
} while( t <= (1LL << 33) );
Ускорение здесь получается тремя способами: предварительно вычисленное начальное значение (эквивалентное ~ 10 итерациям цикла), более ранний выход из цикла и пропуск некоторых значений t. В последней части я рассмотрю z = r - x * x
и задаю t как наибольшую степень деления z на 2 с небольшим фокусом. Это позволяет мне пропустить t значений, которые не повлияли бы на значение r в любом случае. Предварительно вычисленное начальное значение в моем случае выбирает «наименьший положительный» квадратный корень по модулю 8192. Даже если этот код не работает для вас быстрее, я надеюсь, вам понравятся некоторые идеи, которые он содержит , Далее следует полный, проверенный код, включая предварительно вычисленные таблицы.
typedef signed long long int int64;
int start[1024] =
{1,3,1769,5,1937,1741,7,1451,479,157,9,91,945,659,1817,11,
1983,707,1321,1211,1071,13,1479,405,415,1501,1609,741,15,339,1703,203,
129,1411,873,1669,17,1715,1145,1835,351,1251,887,1573,975,19,1127,395,
1855,1981,425,453,1105,653,327,21,287,93,713,1691,1935,301,551,587,
257,1277,23,763,1903,1075,1799,1877,223,1437,1783,859,1201,621,25,779,
1727,573,471,1979,815,1293,825,363,159,1315,183,27,241,941,601,971,
385,131,919,901,273,435,647,1493,95,29,1417,805,719,1261,1177,1163,
1599,835,1367,315,1361,1933,1977,747,31,1373,1079,1637,1679,1581,1753,1355,
513,1539,1815,1531,1647,205,505,1109,33,1379,521,1627,1457,1901,1767,1547,
1471,1853,1833,1349,559,1523,967,1131,97,35,1975,795,497,1875,1191,1739,
641,1149,1385,133,529,845,1657,725,161,1309,375,37,463,1555,615,1931,
1343,445,937,1083,1617,883,185,1515,225,1443,1225,869,1423,1235,39,1973,
769,259,489,1797,1391,1485,1287,341,289,99,1271,1701,1713,915,537,1781,
1215,963,41,581,303,243,1337,1899,353,1245,329,1563,753,595,1113,1589,
897,1667,407,635,785,1971,135,43,417,1507,1929,731,207,275,1689,1397,
1087,1725,855,1851,1873,397,1607,1813,481,163,567,101,1167,45,1831,1205,
1025,1021,1303,1029,1135,1331,1017,427,545,1181,1033,933,1969,365,1255,1013,
959,317,1751,187,47,1037,455,1429,609,1571,1463,1765,1009,685,679,821,
1153,387,1897,1403,1041,691,1927,811,673,227,137,1499,49,1005,103,629,
831,1091,1449,1477,1967,1677,697,1045,737,1117,1737,667,911,1325,473,437,
1281,1795,1001,261,879,51,775,1195,801,1635,759,165,1871,1645,1049,245,
703,1597,553,955,209,1779,1849,661,865,291,841,997,1265,1965,1625,53,
1409,893,105,1925,1297,589,377,1579,929,1053,1655,1829,305,1811,1895,139,
575,189,343,709,1711,1139,1095,277,993,1699,55,1435,655,1491,1319,331,
1537,515,791,507,623,1229,1529,1963,1057,355,1545,603,1615,1171,743,523,
447,1219,1239,1723,465,499,57,107,1121,989,951,229,1521,851,167,715,
1665,1923,1687,1157,1553,1869,1415,1749,1185,1763,649,1061,561,531,409,907,
319,1469,1961,59,1455,141,1209,491,1249,419,1847,1893,399,211,985,1099,
1793,765,1513,1275,367,1587,263,1365,1313,925,247,1371,1359,109,1561,1291,
191,61,1065,1605,721,781,1735,875,1377,1827,1353,539,1777,429,1959,1483,
1921,643,617,389,1809,947,889,981,1441,483,1143,293,817,749,1383,1675,
63,1347,169,827,1199,1421,583,1259,1505,861,457,1125,143,1069,807,1867,
2047,2045,279,2043,111,307,2041,597,1569,1891,2039,1957,1103,1389,231,2037,
65,1341,727,837,977,2035,569,1643,1633,547,439,1307,2033,1709,345,1845,
1919,637,1175,379,2031,333,903,213,1697,797,1161,475,1073,2029,921,1653,
193,67,1623,1595,943,1395,1721,2027,1761,1955,1335,357,113,1747,1497,1461,
1791,771,2025,1285,145,973,249,171,1825,611,265,1189,847,1427,2023,1269,
321,1475,1577,69,1233,755,1223,1685,1889,733,1865,2021,1807,1107,1447,1077,
1663,1917,1129,1147,1775,1613,1401,555,1953,2019,631,1243,1329,787,871,885,
449,1213,681,1733,687,115,71,1301,2017,675,969,411,369,467,295,693,
1535,509,233,517,401,1843,1543,939,2015,669,1527,421,591,147,281,501,
577,195,215,699,1489,525,1081,917,1951,2013,73,1253,1551,173,857,309,
1407,899,663,1915,1519,1203,391,1323,1887,739,1673,2011,1585,493,1433,117,
705,1603,1111,965,431,1165,1863,533,1823,605,823,1179,625,813,2009,75,
1279,1789,1559,251,657,563,761,1707,1759,1949,777,347,335,1133,1511,267,
833,1085,2007,1467,1745,1805,711,149,1695,803,1719,485,1295,1453,935,459,
1151,381,1641,1413,1263,77,1913,2005,1631,541,119,1317,1841,1773,359,651,
961,323,1193,197,175,1651,441,235,1567,1885,1481,1947,881,2003,217,843,
1023,1027,745,1019,913,717,1031,1621,1503,867,1015,1115,79,1683,793,1035,
1089,1731,297,1861,2001,1011,1593,619,1439,477,585,283,1039,1363,1369,1227,
895,1661,151,645,1007,1357,121,1237,1375,1821,1911,549,1999,1043,1945,1419,
1217,957,599,571,81,371,1351,1003,1311,931,311,1381,1137,723,1575,1611,
767,253,1047,1787,1169,1997,1273,853,1247,413,1289,1883,177,403,999,1803,
1345,451,1495,1093,1839,269,199,1387,1183,1757,1207,1051,783,83,423,1995,
639,1155,1943,123,751,1459,1671,469,1119,995,393,219,1743,237,153,1909,
1473,1859,1705,1339,337,909,953,1771,1055,349,1993,613,1393,557,729,1717,
511,1533,1257,1541,1425,819,519,85,991,1693,503,1445,433,877,1305,1525,
1601,829,809,325,1583,1549,1991,1941,927,1059,1097,1819,527,1197,1881,1333,
383,125,361,891,495,179,633,299,863,285,1399,987,1487,1517,1639,1141,
1729,579,87,1989,593,1907,839,1557,799,1629,201,155,1649,1837,1063,949,
255,1283,535,773,1681,461,1785,683,735,1123,1801,677,689,1939,487,757,
1857,1987,983,443,1327,1267,313,1173,671,221,695,1509,271,1619,89,565,
127,1405,1431,1659,239,1101,1159,1067,607,1565,905,1755,1231,1299,665,373,
1985,701,1879,1221,849,627,1465,789,543,1187,1591,923,1905,979,1241,181};
bool bad255[512] =
{0,0,1,1,0,1,1,1,1,0,1,1,1,1,1,0,0,1,1,0,1,0,1,1,1,0,1,1,1,1,0,1,
1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1,1,1,1,0,1,1,1,
0,1,0,1,1,0,0,1,1,1,1,1,0,1,1,1,1,0,1,1,0,0,1,1,1,1,1,1,1,1,0,1,
1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1,1,1,0,1,1,1,1,0,0,1,1,1,1,1,1,
1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,
1,1,1,1,1,1,0,1,1,0,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,
1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,
1,0,1,1,1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,
0,0,1,1,0,1,1,1,1,0,1,1,1,1,1,0,0,1,1,0,1,0,1,1,1,0,1,1,1,1,0,1,
1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1,1,1,1,0,1,1,1,
0,1,0,1,1,0,0,1,1,1,1,1,0,1,1,1,1,0,1,1,0,0,1,1,1,1,1,1,1,1,0,1,
1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1,1,1,0,1,1,1,1,0,0,1,1,1,1,1,1,
1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,
1,1,1,1,1,1,0,1,1,0,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,
1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,
1,0,1,1,1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,
0,0};
inline bool square( int64 x ) {
// Quickfail
if( x < 0 || (x&2) || ((x & 7) == 5) || ((x & 11) == 8) )
return false;
if( x == 0 )
return true;
// Check mod 255 = 3 * 5 * 17, for fun
int64 y = x;
y = (y & 4294967295LL) + (y >> 32);
y = (y & 65535) + (y >> 16);
y = (y & 255) + ((y >> 8) & 255) + (y >> 16);
if( bad255[y] )
return false;
// Divide out powers of 4 using binary search
if((x & 4294967295LL) == 0)
x >>= 32;
if((x & 65535) == 0)
x >>= 16;
if((x & 255) == 0)
x >>= 8;
if((x & 15) == 0)
x >>= 4;
if((x & 3) == 0)
x >>= 2;
if((x & 7) != 1)
return false;
// Compute sqrt using something like Hensel's lemma
int64 r, t, z;
r = start[(x >> 3) & 1023];
do {
z = x - r * r;
if( z == 0 )
return true;
if( z < 0 )
return false;
t = z & (-z);
r += (z & t) >> 1;
if( r > (t >> 1) )
r = t - r;
} while( t <= (1LL << 33) );
return false;
}
Я не знаю, упоминалось ли это ранее. Но я нашел решение здесь :
int result = (int)(floor(sqrt(b)) - ceil(sqrt(a)) + 1);
Если скорость является беспокойством, почему бы не отгородить перегородкой обычно используемый набор исходных данных и их значений к таблице поиска и затем делает любой оптимизированный волшебный алгоритм, Вы придумали для исключительных случаев?
Должна быть возможность упаковать 'не может быть идеальным квадратом, если последние X цифр N' гораздо эффективнее, чем это! Я буду использовать 32-битные числа java и получу достаточно данных для проверки последних 16 битов числа - это 2048 шестнадцатеричных значений типа int.
...
Хорошо. Либо я столкнулся с некоторой теорией чисел, которая немного выше меня, либо в моем коде есть ошибка. В любом случае, вот код:
public static void main(String[] args) {
final int BITS = 16;
BitSet foo = new BitSet();
for(int i = 0; i< (1<<BITS); i++) {
int sq = (i*i);
sq = sq & ((1<<BITS)-1);
foo.set(sq);
}
System.out.println("int[] mayBeASquare = {");
for(int i = 0; i< 1<<(BITS-5); i++) {
int kk = 0;
for(int j = 0; j<32; j++) {
if(foo.get((i << 5) | j)) {
kk |= 1<<j;
}
}
System.out.print("0x" + Integer.toHexString(kk) + ", ");
if(i%8 == 7) System.out.println();
}
System.out.println("};");
}
и вот результаты:
(изд: исключен из-за низкой производительности в prettify.js; просмотрите историю изменений, чтобы увидеть.)
Вот решение «разделяй и властвуй».
Если квадратный корень из натурального числа (number
) является натуральным числом (solution
), вы можете легко определить диапазон для solution
на основе количества цифр в number
:
number
имеет 1 цифру: solution
в диапазоне = 1 - 4 number
имеет 2 цифры: solution
в диапазоне = 3 - 10 number
имеет 3 цифры: solution
в диапазоне = 10 - 40 number
имеет 4 цифры: solution
в диапазоне = 30 - 100 number
имеет 5 цифр: solution
в диапазоне = 100 - 400 Заметить повторение?
Вы можете использовать этот диапазон в подходе двоичного поиска, чтобы увидеть, существует ли solution
] для которого:
number == solution * solution
Вот код
Вот мой класс SquareRootChecker
public class SquareRootChecker {
private long number;
private long initialLow;
private long initialHigh;
public SquareRootChecker(long number) {
this.number = number;
initialLow = 1;
initialHigh = 4;
if (Long.toString(number).length() % 2 == 0) {
initialLow = 3;
initialHigh = 10;
}
for (long i = 0; i < Long.toString(number).length() / 2; i++) {
initialLow *= 10;
initialHigh *= 10;
}
if (Long.toString(number).length() % 2 == 0) {
initialLow /= 10;
initialHigh /=10;
}
}
public boolean checkSquareRoot() {
return findSquareRoot(initialLow, initialHigh, number);
}
private boolean findSquareRoot(long low, long high, long number) {
long check = low + (high - low) / 2;
if (high >= low) {
if (number == check * check) {
return true;
}
else if (number < check * check) {
high = check - 1;
return findSquareRoot(low, high, number);
}
else {
low = check + 1;
return findSquareRoot(low, high, number);
}
}
return false;
}
}
А вот пример того, как использовать его.
long number = 1234567;
long square = number * number;
SquareRootChecker squareRootChecker = new SquareRootChecker(square);
System.out.println(square + ": " + squareRootChecker.checkSquareRoot()); //Prints "1524155677489: true"
long notSquare = square + 1;
squareRootChecker = new SquareRootChecker(notSquare);
System.out.println(notSquare + ": " + squareRootChecker.checkSquareRoot()); //Prints "1524155677490: false"
Если вам нужна скорость, учитывая, что ваши целые числа имеют конечный размер, я подозреваю, что самый быстрый способ заключался бы в (а) разбиении параметров по размеру (например, на категории по величине набора битов), а затем проверке значения по массиву идеальных квадратов в этом диапазоне.
Если вы хотите избежать нецелочисленных операций, вы можете использовать метод ниже. В основном он использует метод Ньютона, модифицированный для целочисленной арифметики.
/**
* Test if the given number is a perfect square.
* @param n Must be greater than 0 and less
* than Long.MAX_VALUE.
* @return <code>true</code> if n is a perfect
* square, or <code>false</code> otherwise.
*/
public static boolean isSquare(long n)
{
long x1 = n;
long x2 = 1L;
while (x1 > x2)
{
x1 = (x1 + x2) / 2L;
x2 = n / x1;
}
return x1 == x2 && n % x1 == 0L;
}
Эта реализация не может конкурировать с решениями, которые используют Math.sqrt
. Однако его производительность можно улучшить, используя механизмы фильтрации, описанные в некоторых других публикациях.
Может быть лучшим алгоритмом для задачи является алгоритм быстрого целочисленного квадратного корня https://stackoverflow.com/a/51585204/5191852
Там @Kde утверждает, что три итерации метода Ньютона было бы достаточно для точности ± 1 для 32-битных целых чисел. Конечно, для 64-битных целых чисел требуется больше итераций, может быть 6 или 7.
Не знаю о самом быстром, но самое простое - взять квадратный корень обычным способом, умножить результат на себя и посмотреть, соответствует ли оно вашему первоначальному значению.
Так как мы говорим здесь целые числа, пост может включать коллекцию, в которой вы можете просто найти.
Мне нравится идея использовать почти правильный метод для некоторых входных данных. Вот версия с более высоким «смещением». Кажется, код работает и проходит мой простой тестовый пример.
Просто замените ваш:
if(n < 410881L){...}
код на этот:
if (n < 11043908100L) {
//John Carmack hack, converted to Java.
// See: http://www.codemaestro.com/reviews/9
int i;
float x2, y;
x2 = n * 0.5F;
y = n;
i = Float.floatToRawIntBits(y);
//using the magic number from
//http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf
//since it more accurate
i = 0x5f375a86 - (i >> 1);
y = Float.intBitsToFloat(i);
y = y * (1.5F - (x2 * y * y));
y = y * (1.5F - (x2 * y * y)); //Newton iteration, more accurate
sqrt = Math.round(1.0F / y);
} else {
//Carmack hack gives incorrect answer for n >= 11043908100.
sqrt = (long) Math.sqrt(n);
}
Это доработка от десятичного к двоичному алгоритму старого калькулятора Марчанта (извините, у меня нет ссылки) в Ruby, адаптированном специально для этого вопроса:
def isexactsqrt(v)
value = v.abs
residue = value
root = 0
onebit = 1
onebit <<= 8 while (onebit < residue)
onebit >>= 2 while (onebit > residue)
while (onebit > 0)
x = root + onebit
if (residue >= x) then
residue -= x
root = x + onebit
end
root >>= 1
onebit >>= 2
end
return (residue == 0)
end
Вот обработка что-то похожее (пожалуйста, не голосуйте за стиль кодирования / запахи или неуклюжий ввод-вывод - это алгоритм, который имеет значение, а C ++ не является моим родным языком). В этом случае мы ищем остаток == 0:
#include <iostream>
using namespace std;
typedef unsigned long long int llint;
class ISqrt { // Integer Square Root
llint value; // Integer whose square root is required
llint root; // Result: floor(sqrt(value))
llint residue; // Result: value-root*root
llint onebit, x; // Working bit, working value
public:
ISqrt(llint v = 2) { // Constructor
Root(v); // Take the root
};
llint Root(llint r) { // Resets and calculates new square root
value = r; // Store input
residue = value; // Initialise for subtracting down
root = 0; // Clear root accumulator
onebit = 1; // Calculate start value of counter
onebit <<= (8*sizeof(llint)-2); // Set up counter bit as greatest odd power of 2
while (onebit > residue) {onebit >>= 2; }; // Shift down until just < value
while (onebit > 0) {
x = root ^ onebit; // Will check root+1bit (root bit corresponding to onebit is always zero)
if (residue >= x) { // Room to subtract?
residue -= x; // Yes - deduct from residue
root = x + onebit; // and step root
};
root >>= 1;
onebit >>= 2;
};
return root;
};
llint Residue() { // Returns residue from last calculation
return residue;
};
};
int main() {
llint big, i, q, r, v, delta;
big = 0; big = (big-1); // Kludge for "big number"
ISqrt b; // Make q sqrt generator
for ( i = big; i > 0 ; i /= 7 ) { // for several numbers
q = b.Root(i); // Get the square root
r = b.Residue(); // Get the residue
v = q*q+r; // Recalc original value
delta = v-i; // And diff, hopefully 0
cout << i << ": " << q << " ++ " << r << " V: " << v << " Delta: " << delta << "\n";
};
return 0;
};
Я думал об ужасных временах, которые я провел в Числовом Аналитическом ходе.
И затем я помню, было это окружение функции вокруг 'сети от Исходного кода Quake:
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // wtf?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
#ifndef Q3_VM
#ifdef __linux__
assert( !isnan(y) ); // bk010122 - FPE?
#endif
#endif
return y;
}
, Который в основном вычисляет квадратный корень, с помощью функции приближения Newton (наклон помнят точное имя).
Это должно быть применимым и могло бы даже быть быстрее, это от одной из феноменальной идентификационной игры программного обеспечения!
Это записано в C++, но не должно быть слишком трудно снова использовать ту же технику в Java, как только Вы получаете идею:
я первоначально нашел его в: http://www.codemaestro.com/reviews/9
метод Newton объяснил в Википедии: http://en.wikipedia.org/wiki/Newton%27s_method
можно перейти по ссылке для большего количества объяснения того, как это работает, но если Вы не заботитесь очень, тогда это примерно, что я помню от чтения блога и от взятия Числового Аналитического хода:
* (long*) &y
в основном быстрая функция convert-long, таким образом, целочисленные операции могут быть применены на необработанные байты. 0x5f3759df - (i >> 1);
строка является предрасчетным значением семени для функции приближения. * (float*) &i
преобразовывает значение назад в плавающую точку. y = y * ( threehalfs - ( x2 * y * y ) )
строка в основном выполняет итерации значения по функции снова. функция приближения дает более точные значения, больше Вы выполняете итерации функции по результату. В случае Quake одно повторение "достаточно хорошо", но если бы это не было для Вас... тогда, то Вы могли бы добавить столько повторения, сколько Вам нужно.
Это должно быть быстрее, потому что это сокращает количество операций деления, сделанных в наивном квадрате, базирующемся вниз к простому делению 2 (на самом деле * 0.5F
, умножают операцию), и замените его некоторыми постоянное число операций умножения вместо этого.
Вам нужно будет сделать несколько тестов. Лучший алгоритм будет зависеть от распределения ваших входных данных.
Ваш алгоритм может быть почти оптимальным, но вы можете сделать быструю проверку, чтобы исключить некоторые возможности, прежде чем вызывать подпрограмму с квадратным корнем. Например, посмотрите на последнюю цифру вашего числа в шестнадцатеричном виде, выполнив побитовое «и». Совершенные квадраты могут заканчиваться только 0, 1, 4 или 9 в основании 16, так что для 75% ваших входных данных (при условии, что они распределены равномерно) вы можете избежать вызова квадратного корня в обмен на какое-то очень быстрое переключение битов.
Кип протестировал следующий код, реализующий шестнадцатеричный трюк. При тестировании чисел от 1 до 100 000 000 этот код выполнялся в два раза быстрее оригинала.
public final static boolean isPerfectSquare(long n)
{
if (n < 0)
return false;
switch((int)(n & 0xF))
{
case 0: case 1: case 4: case 9:
long tst = (long)Math.sqrt(n);
return tst*tst == n;
default:
return false;
}
}
Когда я тестировал аналогичный код в C ++, он на самом деле работал медленнее, чем оригинал. Однако когда я исключил оператор switch, шестнадцатеричный трюк снова сделал код в два раза быстрее.
int isPerfectSquare(int n)
{
int h = n & 0xF; // h is the last hex "digit"
if (h > 9)
return 0;
// Use lazy evaluation to jump out of the if statement as soon as possible
if (h != 2 && h != 3 && h != 5 && h != 6 && h != 7 && h != 8)
{
int t = (int) floor( sqrt((double) n) + 0.5 );
return t*t == n;
}
return 0;
}
Устранение оператора switch мало повлияло на код C #.
Я не уверен, что это будет быстрее или даже точнее, но вы могли бы использовать алгоритм магического квадратного корня Джона Кармака , чтобы быстрее решить квадратный корень. Вероятно, вы могли бы легко проверить это для всех возможных 32-битных целых чисел и убедиться, что вы действительно получили правильные результаты, так как это всего лишь приближение. Тем не менее, теперь, когда я думаю об этом, использование двойных чисел также приближенно, поэтому я не уверен, каким образом это вступит в игру.
Если Вы делаете поиск делением пополам, чтобы попытаться найти "правильный" квадратный корень, можно довольно легко обнаружить, если значение, которое Вы имеете, достаточно близко для сообщения:
(n+1)^2 = n^2 + 2n + 1
(n-1)^2 = n^2 - 2n + 1
Так вычислявший n^2
, опции:
n^2 = target
: сделанный, возвратитесь верный n^2 + 2n + 1 > target > n^2
: Вы близки, но это не прекрасно: возвратите false n^2 - 2n + 1 < target < n^2
: так же target < n^2 - 2n + 1
: поиск делением пополам на более низком n
target > n^2 + 2n + 1
: поиск делением пополам на более высоком n
(Извините, это использует n
в качестве Вашего текущего предположения, и target
для параметра. Принесите извинения за беспорядок!)
я не знаю, будет ли это быстрее или нет, но это стоит попытки.
РЕДАКТИРОВАНИЕ: поиск делением пополам не должен брать в целом диапазоне целых чисел, ни один (2^x)^2 = 2^(2x)
, поэтому как только Вы нашли, что главный набор укусил в Вашей цели (который может быть сделан с приемом битового жонглирования; я забываю точно, как) можно быстро получить диапазон потенциальных ответов. Следите за Вами, наивный поиск делением пополам все еще только собирается взять до 31 или 32 повторения.
Я хочу, чтобы эта функция работала со всеми положительными 64-битными целыми числами со знаком
Math.sqrt()
работает с двойными числами в качестве входных параметров, поэтому вы не получите точных результатов для целых чисел большего размера чем 2 ^ 53 .
Это должно быть намного быстрее для использования метод Newton для вычисления Целочисленный Квадратный корень , затем возвести в квадрат это число и проверку, как Вы делаете в своем текущем решении. Метод Newton является основанием для решения Carmack, упомянутого в некоторых других ответах. Необходимо быть в состоянии получить более быстрый ответ, так как Вы только интересуетесь целой частью корня, позволяя Вам остановить алгоритм аппроксимации раньше.
Другая оптимизация, которую можно попробовать: Если Цифровой Корень из числа не заканчивается в 1, 4, 7, или 9, число не полный квадрат. Это может использоваться в качестве быстрого способа устранить 60% Ваших исходных данных прежде, чем применить более медленный алгоритм квадратного корня.
Было указано, что последнее d
цифры полного квадрата может только взять определенные значения. Последнее d
цифры (в основе b
) номера n
совпадают с остатком, когда n
разделен на b
<глоток> d
глоток>, т.е. в нотации n % pow(b, d)
.
C Это может быть обобщено к любому модулю m
, т.е. n % m
может использоваться для исключения некоторого процента чисел от того, чтобы быть полными квадратами. Модуль, который Вы в настоящее время используете, равняется 64, который позволяет 12, т.е. 19% остатков, как возможные квадраты. С небольшим кодированием я нашел модуль 110880, который позволяет только 2016, т.е. 1,8% остатков как возможные квадраты. Таким образом в зависимости от стоимости операции модуля (т.е. подразделение) и поиск по таблице по сравнению с квадратным корнем на Вашей машине, с помощью этого модуля могло бы быть быстрее.
Между прочим, если Java имеет способ сохранить упакованный массив битов для таблицы поиска, не используйте его. 110 880 32-разрядных слов не являются большим количеством RAM в эти дни, и выборка машинного слова будет быстрее, чем выборка единственного бита.
Для производительности вам очень часто приходится идти на некоторые компромиссы. Другие выразили различные методы, однако вы заметили, что хак Кармака был быстрее до определенных значений N. Затем вы должны проверить «n», и если оно меньше, чем число N, используйте хак Кармака, иначе используйте какой-то другой описанный метод в ответах здесь.
Я провел собственный анализ нескольких алгоритмов в этой теме и получил некоторые новые результаты. Вы можете увидеть эти старые результаты в истории редактирования этого ответа, но они не точные, так как я допустил ошибку и потратил время на анализ нескольких алгоритмов, которые не являются близкими. Однако, извлекая уроки из нескольких разных ответов, у меня теперь есть два алгоритма, которые сокрушают «победителя» этой темы. Вот основная вещь, которую я делаю по-другому, чем все остальные:
// This is faster because a number is divisible by 2^4 or more only 6% of the time
// and more than that a vanishingly small percentage.
while((x & 0x3) == 0) x >>= 2;
// This is effectively the same as the switch-case statement used in the original
// answer.
if((x & 0x7) != 1) return false;
Однако эта простая строка, которая в большинстве случаев добавляет одну или две очень быстрые инструкции, значительно упрощает инструкцию switch-case
в одну инструкцию if , Тем не менее, это может добавить к времени выполнения, если многие из протестированных чисел имеют значительную степень двух факторов.
Ниже приведены алгоритмы:
Вот примерное время выполнения, если числа генерируются с использованием Math.abs(java.util.Random.nextLong())
0% Scenario{vm=java, trial=0, benchmark=Internet} 39673.40 ns; ?=378.78 ns @ 3 trials
33% Scenario{vm=java, trial=0, benchmark=Durron} 37785.75 ns; ?=478.86 ns @ 10 trials
67% Scenario{vm=java, trial=0, benchmark=DurronTwo} 35978.10 ns; ?=734.10 ns @ 10 trials
benchmark us linear runtime
Internet 39.7 ==============================
Durron 37.8 ============================
DurronTwo 36.0 ===========================
vm: java
trial: 0
А вот примерное время выполнения, если оно запускается только для первого миллиона длинных:
0% Scenario{vm=java, trial=0, benchmark=Internet} 2933380.84 ns; ?=56939.84 ns @ 10 trials
33% Scenario{vm=java, trial=0, benchmark=Durron} 2243266.81 ns; ?=50537.62 ns @ 10 trials
67% Scenario{vm=java, trial=0, benchmark=DurronTwo} 3159227.68 ns; ?=10766.22 ns @ 3 trials
benchmark ms linear runtime
Internet 2.93 ===========================
Durron 2.24 =====================
DurronTwo 3.16 ==============================
vm: java
trial: 0
Как вы можете видеть, DurronTwo
работает лучше для больших входов, потому что он очень часто использует магический трюк, но затупляется по сравнению с первым алгоритмом и Math.sqrt
, потому что числа намного меньше. Между тем, более простой Durron
является огромным победителем, потому что ему никогда не приходится делить на 4 много много раз в первом миллионном числе.
Вот Durron
:
public final static boolean isPerfectSquareDurron(long n) {
if(n < 0) return false;
if(n == 0) return true;
long x = n;
// This is faster because a number is divisible by 16 only 6% of the time
// and more than that a vanishingly small percentage.
while((x & 0x3) == 0) x >>= 2;
// This is effectively the same as the switch-case statement used in the original
// answer.
if((x & 0x7) == 1) {
long sqrt;
if(x < 410881L)
{
int i;
float x2, y;
x2 = x * 0.5F;
y = x;
i = Float.floatToRawIntBits(y);
i = 0x5f3759df - ( i >> 1 );
y = Float.intBitsToFloat(i);
y = y * ( 1.5F - ( x2 * y * y ) );
sqrt = (long)(1.0F/y);
} else {
sqrt = (long) Math.sqrt(x);
}
return sqrt*sqrt == x;
}
return false;
}
И DurronTwo
public final static boolean isPerfectSquareDurronTwo(long n) {
if(n < 0) return false;
// Needed to prevent infinite loop
if(n == 0) return true;
long x = n;
while((x & 0x3) == 0) x >>= 2;
if((x & 0x7) == 1) {
long sqrt;
if (x < 41529141369L) {
int i;
float x2, y;
x2 = x * 0.5F;
y = x;
i = Float.floatToRawIntBits(y);
//using the magic number from
//http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf
//since it more accurate
i = 0x5f375a86 - (i >> 1);
y = Float.intBitsToFloat(i);
y = y * (1.5F - (x2 * y * y));
y = y * (1.5F - (x2 * y * y)); //Newton iteration, more accurate
sqrt = (long) ((1.0F/y) + 0.2);
} else {
//Carmack hack gives incorrect answer for n >= 41529141369.
sqrt = (long) Math.sqrt(x);
}
return sqrt*sqrt == x;
}
return false;
}
И мой тестовый жгут: (Требуется Google Caliper 0.1-RC5)
public class SquareRootBenchmark {
public static class Benchmark1 extends SimpleBenchmark {
private static final int ARRAY_SIZE = 10000;
long[] trials = new long[ARRAY_SIZE];
@Override
protected void setUp() throws Exception {
Random r = new Random();
for (int i = 0; i < ARRAY_SIZE; i++) {
trials[i] = Math.abs(r.nextLong());
}
}
public int timeInternet(int reps) {
int trues = 0;
for(int i = 0; i < reps; i++) {
for(int j = 0; j < ARRAY_SIZE; j++) {
if(SquareRootAlgs.isPerfectSquareInternet(trials[j])) trues++;
}
}
return trues;
}
public int timeDurron(int reps) {
int trues = 0;
for(int i = 0; i < reps; i++) {
for(int j = 0; j < ARRAY_SIZE; j++) {
if(SquareRootAlgs.isPerfectSquareDurron(trials[j])) trues++;
}
}
return trues;
}
public int timeDurronTwo(int reps) {
int trues = 0;
for(int i = 0; i < reps; i++) {
for(int j = 0; j < ARRAY_SIZE; j++) {
if(SquareRootAlgs.isPerfectSquareDurronTwo(trials[j])) trues++;
}
}
return trues;
}
}
public static void main(String... args) {
Runner.main(Benchmark1.class, args);
}
}
ОБНОВЛЕНИЕ: Я создал новый алгоритм, который быстрее в некоторых сценариях, медленнее в других, я получил разные тесты, основанные на разных входах. Если мы вычислим по модулю 0xFFFFFF = 3 x 3 x 5 x 7 x 13 x 17 x 241
, мы можем исключить 97,82% чисел, которые не могут быть квадратами. Это может быть (в некотором роде) выполнено в одной строке с 5 побитовыми операциями:
if (!goodLookupSquares[(int) ((n & 0xFFFFFFl) + ((n >> 24) & 0xFFFFFFl) + (n >> 48))]) return false;
Результирующий индекс - это либо 1) остаток, 2) остаток + 0xFFFFFF
, либо 3) остаток + 0x1FFFFFE
. Конечно, нам нужно иметь таблицу поиска для остатков по модулю 0xFFFFFF
, которая составляет около 3 МБ файла (в этом случае сохраняются как десятичные числа в тексте ascii, не оптимальные, но явно улучшаемые с помощью ByteBuffer
и т. Д.). Но так как это предварительный расчет, это не имеет большого значения. Вы можете найти файл здесь (или сгенерировать его самостоятельно):
public final static boolean isPerfectSquareDurronThree(long n) {
if(n < 0) return false;
if(n == 0) return true;
long x = n;
while((x & 0x3) == 0) x >>= 2;
if((x & 0x7) == 1) {
if (!goodLookupSquares[(int) ((n & 0xFFFFFFl) + ((n >> 24) & 0xFFFFFFl) + (n >> 48))]) return false;
long sqrt;
if(x < 410881L)
{
int i;
float x2, y;
x2 = x * 0.5F;
y = x;
i = Float.floatToRawIntBits(y);
i = 0x5f3759df - ( i >> 1 );
y = Float.intBitsToFloat(i);
y = y * ( 1.5F - ( x2 * y * y ) );
sqrt = (long)(1.0F/y);
} else {
sqrt = (long) Math.sqrt(x);
}
return sqrt*sqrt == x;
}
return false;
}
Я загружаю его в массив boolean
, как этот :
private static boolean[] goodLookupSquares = null;
public static void initGoodLookupSquares() throws Exception {
Scanner s = new Scanner(new File("24residues_squares.txt"));
goodLookupSquares = new boolean[0x1FFFFFE];
while(s.hasNextLine()) {
int residue = Integer.valueOf(s.nextLine());
goodLookupSquares[residue] = true;
goodLookupSquares[residue + 0xFFFFFF] = true;
goodLookupSquares[residue + 0x1FFFFFE] = true;
}
s.close();
}
Пример времени выполнения. Он побил Durron
(первая версия) в каждом пробном запуске.
0% Scenario{vm=java, trial=0, benchmark=Internet} 40665.77 ns; ?=566.71 ns @ 10 trials
33% Scenario{vm=java, trial=0, benchmark=Durron} 38397.60 ns; ?=784.30 ns @ 10 trials
67% Scenario{vm=java, trial=0, benchmark=DurronThree} 36171.46 ns; ?=693.02 ns @ 10 trials
benchmark us linear runtime
Internet 40.7 ==============================
Durron 38.4 ============================
DurronThree 36.2 ==========================
vm: java
trial: 0
Просто для протокола, другой подход заключается в использовании простого разложения. Если каждый фактор разложения четный, то число является идеальным квадратом. Итак, вы хотите посмотреть, можно ли разложить число как произведение квадратов простых чисел. Конечно, вам не нужно получать такое разложение, просто чтобы увидеть, существует ли оно.
Сначала создайте таблицу квадратов простых чисел, которые меньше, чем 2 ^ 32. Это намного меньше, чем таблица всех целых чисел до этого предела.
Решение тогда будет таким:
boolean isPerfectSquare(long number)
{
if (number < 0) return false;
if (number < 2) return true;
for (int i = 0; ; i++)
{
long square = squareTable[i];
if (square > number) return false;
while (number % square == 0)
{
number /= square;
}
if (number == 1) return true;
}
}
Я думаю, это немного загадочно. На каждом шаге он проверяет, что квадрат простого числа делит входное число. Если это так, то он делит число на квадрат как можно дольше, чтобы удалить этот квадрат из простого разложения. Если в результате этого процесса мы пришли к 1, то входное число было разложением квадрата простых чисел. Если квадрат становится больше, чем само число, то этот квадрат или более крупные квадраты никак не могут его разделить, поэтому число не может быть разложением квадратов простых чисел.
Учитывая, что в настоящее время sqrt выполняется аппаратно, и здесь необходимо вычислять простые числа, я думаю, что это решение намного медленнее. Но это должно дать лучшие результаты, чем решение с sqrt, которое не будет работать более 2 ^ 54, как говорит mrzl в своем ответе.
Необходимо избавиться от части с 2 питанием N с самого начала.
2-е Редактирование волшебное выражение для m ниже должно быть
m = N - (N & (N-1));
и не, как записано
Конец 2-го редактирования
m = N & (N-1); // the lawest bit of N
N /= m;
byte = N & 0x0F;
if ((m % 2) || (byte !=1 && byte !=9))
return false;
1-е Редактирование:
Незначительное улучшение:
m = N & (N-1); // the lawest bit of N
N /= m;
if ((m % 2) || (N & 0x07 != 1))
return false;
Конец 1-го редактирования
Теперь продолжаются, как обычно. Таким образом, к тому времени, когда Вы добираетесь до части с плавающей точкой, Вы уже, избавился от всех чисел, чья часть с 2 питанием нечетна (приблизительно половина), и затем Вы только рассматриваете 1/8 того, что оставляют. Т.е. Вы выполняете часть с плавающей точкой на 6% чисел.
Относительно метода Carmac кажется, что было бы довольно легко только выполнить итерации еще раз, который должен удвоить количество цифр точности. Это - в конце концов, чрезвычайно усеченный повторяющийся метод - Ньютон с очень хорошим первым предположением.
Относительно Вашего лучшего тока, я вижу две микрооптимизации:
Т.е.:
// Divide out powers of 4 using binary search
if((n & 0x3L) == 0) {
n >>=2;
if((n & 0xffffffffL) == 0)
n >>= 32;
if((n & 0xffffL) == 0)
n >>= 16;
if((n & 0xffL) == 0)
n >>= 8;
if((n & 0xfL) == 0)
n >>= 4;
if((n & 0x3L) == 0)
n >>= 2;
}
Еще лучше могло бы быть простое
while ((n & 0x03L) == 0) n >>= 2;
, Очевидно, будет интересно знать, сколько чисел отобрано в каждой контрольной точке - я скорее сомневаюсь, что проверки действительно независимы, который делает вещи хитрыми.
Вызов sqrt не совершенно точен, как был упомянут, но это интересно и поучительно, что это не сдувает другие ответы с точки зрения скорости. В конце концов, последовательность инструкций по ассемблеру для sqrt является крошечной. Intel имеет аппаратную инструкцию, которая не используется Java, которому я верю, потому что она не соответствует IEEE.
Итак, почему это медленно? Поскольку Java на самом деле называет стандартную программу C через JNI, и это на самом деле медленнее, чтобы сделать так, чем назвать подпрограмму Java, которая саму медленнее, чем выполнение, это встраивает. Это является очень раздражающим, и Java должен был предложить лучшее решение, т.е. создающий в вызовах библиотеки операций с плавающей точкой при необходимости. О, хорошо.
В C++, я подозреваю, что все сложные альтернативы проиграли бы на скорости, но я не проверил их всех. То, что я сделал, и что люди Java найдут полезным, является простым взломом, расширением тестирования особого случая, предложенного A. Король. Используйте единственное длинное значение в качестве небольшого массива, который не является проверенными границами. Тем путем у Вас есть булев поиск на 64 бита.
typedef unsigned long long UVLONG
UVLONG pp1,pp2;
void init2() {
for (int i = 0; i < 64; i++) {
for (int j = 0; j < 64; j++)
if (isPerfectSquare(i * 64 + j)) {
pp1 |= (1 << j);
pp2 |= (1 << i);
break;
}
}
cout << "pp1=" << pp1 << "," << pp2 << "\n";
}
inline bool isPerfectSquare5(UVLONG x) {
return pp1 & (1 << (x & 0x3F)) ? isPerfectSquare(x) : false;
}
стандартная программа isPerfectSquare5 работает приблизительно в 1/3 время на моей core2 машине дуэта. Я подозреваю, что дальнейшие тонкие настройки в том же направлении могли уменьшить время далее в среднем, но каждый раз Вы проверяете, Вы обмениваете больше тестирования на большее количество устранения, таким образом, Вы не можете пойти слишком много дальше на той дороге.
, Конечно, вместо того, чтобы иметь отдельный тест для отрицания, Вы могли проверить высокие 6 битов тот же путь.
Примечание, которое все я делаю, устраняет возможные квадраты, но когда у меня есть потенциальный случай, я должен назвать исходный, встроенный isPerfectSquare.
init2 стандартную программу называют однажды для инициализации статических значений pp1 и pp2. Обратите внимание, что в моей реализации в C++, я использую неподписанный длинный длинный, поэтому так как Вы подписываетесь, необходимо было бы использовать>>> оператор.
нет никакой внутренней потребности к граничной проверке, массив, но оптимизатор Java должен понять этот материал довольно быстро, таким образом, я не обвиняю их в этом.
Euler проекта упоминается в тегах, и многие проблемы в нем требуют чисел проверки>> 2^64
. Большая часть упомянутой выше оптимизации не работает легко, когда Вы работаете с 80-байтовым буфером.
Я использовал java BigInteger и немного измененную версию метода Newton, тот, который работает лучше с целыми числами. Проблема состояла в том что точные квадраты n^2
сходившийся к (n-1)
вместо n
потому что n^2-1 = (n-1)(n+1)
и конечная погрешность была всего одним шагом ниже заключительного делителя и завершенного алгоритма. Было легко зафиксировать путем добавления одного к исходному аргументу прежде, чем вычислить ошибку. (Добавьте два для кубических корней, и т.д.),
Один хороший атрибут этого алгоритма - то, что можно сразу сказать, является ли число полным квадратом - конечная погрешность (не исправление) в методе Newton будет нулем. Простая модификация также позволяет Вам быстро вычислить floor(sqrt(x))
вместо самого близкого целого числа. Это удобно с несколькими Euler проблемами.
Это самая быстрая реализация Java, которую я мог придумать, используя комбинацию методов, предложенных другими в этом потоке.
Я также экспериментировал с этим изменения, но они не улучшили производительность:
public class SquareTester {
public static boolean isPerfectSquare(long n) {
if (n < 0) {
return false;
} else {
switch ((byte) n) {
case -128: case -127: case -124: case -119: case -112:
case -111: case -103: case -95: case -92: case -87:
case -79: case -71: case -64: case -63: case -60:
case -55: case -47: case -39: case -31: case -28:
case -23: case -15: case -7: case 0: case 1:
case 4: case 9: case 16: case 17: case 25:
case 33: case 36: case 41: case 49: case 57:
case 64: case 65: case 68: case 73: case 81:
case 89: case 97: case 100: case 105: case 113:
case 121:
long i = (n * INV3465) >>> 52;
if (! good3465[(int) i]) {
return false;
} else {
long r = round(Math.sqrt(n));
return r*r == n;
}
default:
return false;
}
}
}
private static int round(double x) {
return (int) Double.doubleToRawLongBits(x + (double) (1L << 52));
}
/** 3465<sup>-1</sup> modulo 2<sup>64</sup> */
private static final long INV3465 = 0x8ffed161732e78b9L;
private static final boolean[] good3465 =
new boolean[0x1000];
static {
for (int r = 0; r < 3465; ++ r) {
int i = (int) ((r * r * INV3465) >>> 52);
good3465[i] = good3465[i+1] = true;
}
}
}
"I'm looking for the fastest way to determine if a long value is a perfect square (i.e. its square root is another integer)."
The answers are impressive, but I failed to see a simple check :
check whether the first number on the right of the long it a member of the set (0,1,4,5,6,9) . If it is not, then it cannot possibly be a 'perfect square' .
eg.
4567 - cannot be a perfect square.