Самый быстрый способ определить, является ли целочисленный квадратный корень целым числом

Несмотря на обоснованные причины для свойств, существует общая идея объектно-ориентированного дизайна, заключающаяся в том, что разоблачение состояния участника через свойства является плохим дизайном. Статья Роберта Мартина об Открытом Закрытом Принципе расширяет это, заявляя, что Свойства поощряют связь и поэтому ограничивают возможность закрыть класс от модификации - если вы измените свойство, всем потребителям класса понадобится также измениться. Он квалифицирует, что подвергая переменные-члены не обязательно плохой дизайн, это может быть просто плохой стиль. Однако, если свойства доступны только для чтения, вероятность злоупотреблений и побочных эффектов меньше.

Лучший подход, который я могу предложить для модульного тестирования (и это может показаться странным), - это сделать как можно больше свойств защищенный или внутренний. Это предотвратит связь, не поощряя писать глупые тесты для геттеров и сеттеров.

Есть очевидные причины, по которым должны использоваться свойства чтения / записи, такие как свойства ViewModel, которые привязаны к полям ввода и т. Д.

Более практично, модульные тесты должны обеспечивать функциональность с помощью общедоступных методов. Если код, который вы тестируете, использует эти свойства, вы получаете бесплатное покрытие кода. Если окажется, что эти свойства никогда не будут подсвечиваться с помощью покрытия кода, существует очень сильная возможность:

  1. Вам не хватает тестов, которые косвенно используют свойства
  2. . Свойства unused

Если вы пишете тесты для геттеров и сеттеров, вы получаете ложное ощущение охвата и не сможете определить, действительно ли свойства используются функциональным поведением.

1371
задан 17 revs, 12 users 36% 23 December 2018 в 02:25
поделиться

27 ответов

Я нашел метод, который работает примерно на 35% быстрее, чем ваш код 6bit + Carmack + sqrt, по крайней мере, с моим процессором (x86) и языком программирования (C / C ++). Ваши результаты могут отличаться, особенно потому, что я не знаю, как будет действовать фактор Java.

У меня тройной подход:

  1. Во-первых, отфильтруйте очевидные ответы. Это включает в себя отрицательные числа и глядя на последние 4 бита. (Я обнаружил, что просмотр последних шести не помог.) Я также отвечаю «да» для 0. (Читая код ниже, обратите внимание, что мой ввод int64 x.)
    if( x < 0 || (x&2) || ((x & 7) == 5) || ((x & 11) == 8) )
        return false;
    if( x == 0 )
        return true;
  2. Далее, проверьте если это квадрат по модулю 255 = 3 * 5 * 17. Поскольку это произведение трех различных простых чисел, только около 1/8 из остатков по модулю 255 являются квадратами. Однако, по моему опыту, вызов оператора по модулю (%) стоит больше, чем получаемый эффект, поэтому я использую битовые трюки с 255 = 2 ^ 8-1 для вычисления остатка. (Что бы там ни было, я не использую уловку чтения отдельных байтов из слова, только поразрядно и смещается.)
    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
    
  3. Наконец, попробуйте вычислить квадратный корень, используя метод, аналогичный лемме Хензеля . (Я не думаю, что это применимо напрямую, но оно работает с некоторыми модификациями.) Перед этим я делю все степени 2 с помощью двоичного поиска:
    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;
}
682
ответ дан 4 revs, 2 users 100% 23 December 2018 в 02:25
поделиться

Я не знаю, упоминалось ли это ранее. Но я нашел решение здесь :

int result = (int)(floor(sqrt(b)) - ceil(sqrt(a)) + 1);
2
ответ дан Hemil 23 December 2018 в 02:25
поделиться

Если скорость является беспокойством, почему бы не отгородить перегородкой обычно используемый набор исходных данных и их значений к таблице поиска и затем делает любой оптимизированный волшебный алгоритм, Вы придумали для исключительных случаев?

1
ответ дан Elijah 23 December 2018 в 02:25
поделиться

Должна быть возможность упаковать 'не может быть идеальным квадратом, если последние 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; просмотрите историю изменений, чтобы увидеть.)

1
ответ дан paulmurray 23 December 2018 в 02:25
поделиться

Вот решение «разделяй и властвуй».

Если квадратный корень из натурального числа (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"
1
ответ дан MWB 23 December 2018 в 02:25
поделиться

Если вам нужна скорость, учитывая, что ваши целые числа имеют конечный размер, я подозреваю, что самый быстрый способ заключался бы в (а) разбиении параметров по размеру (например, на категории по величине набора битов), а затем проверке значения по массиву идеальных квадратов в этом диапазоне.

0
ответ дан Celestial M Weasel 23 December 2018 в 02:25
поделиться

Метод Ньютона с целочисленной арифметикой

Если вы хотите избежать нецелочисленных операций, вы можете использовать метод ниже. В основном он использует метод Ньютона, модифицированный для целочисленной арифметики.

/**
 * 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. Однако его производительность можно улучшить, используя механизмы фильтрации, описанные в некоторых других публикациях.

0
ответ дан aventurin 23 December 2018 в 02:25
поделиться

Может быть лучшим алгоритмом для задачи является алгоритм быстрого целочисленного квадратного корня https://stackoverflow.com/a/51585204/5191852

Там @Kde утверждает, что три итерации метода Ньютона было бы достаточно для точности ± 1 для 32-битных целых чисел. Конечно, для 64-битных целых чисел требуется больше итераций, может быть 6 или 7.

0
ответ дан Viktor 23 December 2018 в 02:25
поделиться

Не знаю о самом быстром, но самое простое - взять квадратный корень обычным способом, умножить результат на себя и посмотреть, соответствует ли оно вашему первоначальному значению.

Так как мы говорим здесь целые числа, пост может включать коллекцию, в которой вы можете просто найти.

-3
ответ дан Joel Coehoorn 23 December 2018 в 02:25
поделиться

Мне нравится идея использовать почти правильный метод для некоторых входных данных. Вот версия с более высоким «смещением». Кажется, код работает и проходит мой простой тестовый пример.

Просто замените ваш:

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);
}
6
ответ дан Jonny Heggheim 23 December 2018 в 02:25
поделиться

Это доработка от десятичного к двоичному алгоритму старого калькулятора Марчанта (извините, у меня нет ссылки) в 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;
};
6
ответ дан Brent.Longborough 23 December 2018 в 02:25
поделиться

Я думал об ужасных временах, которые я провел в Числовом Аналитическом ходе.

И затем я помню, было это окружение функции вокруг 'сети от Исходного кода 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, умножают операцию), и замените его некоторыми постоянное число операций умножения вместо этого.

50
ответ дан chakrit 23 December 2018 в 02:25
поделиться

Вам нужно будет сделать несколько тестов. Лучший алгоритм будет зависеть от распределения ваших входных данных.

Ваш алгоритм может быть почти оптимальным, но вы можете сделать быструю проверку, чтобы исключить некоторые возможности, прежде чем вызывать подпрограмму с квадратным корнем. Например, посмотрите на последнюю цифру вашего числа в шестнадцатеричном виде, выполнив побитовое «и». Совершенные квадраты могут заканчиваться только 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 #.

128
ответ дан John D. Cook 23 December 2018 в 02:25
поделиться

Я не уверен, что это будет быстрее или даже точнее, но вы могли бы использовать алгоритм магического квадратного корня Джона Кармака , чтобы быстрее решить квадратный корень. Вероятно, вы могли бы легко проверить это для всех возможных 32-битных целых чисел и убедиться, что вы действительно получили правильные результаты, так как это всего лишь приближение. Тем не менее, теперь, когда я думаю об этом, использование двойных чисел также приближенно, поэтому я не уверен, каким образом это вступит в игру.

36
ответ дан 2 revs, 2 users 86% 23 December 2018 в 02:25
поделиться

Если Вы делаете поиск делением пополам, чтобы попытаться найти "правильный" квадратный корень, можно довольно легко обнаружить, если значение, которое Вы имеете, достаточно близко для сообщения:

(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 повторения.

34
ответ дан Jon Skeet 23 December 2018 в 02:25
поделиться

Я хочу, чтобы эта функция работала со всеми положительными 64-битными целыми числами со знаком

Math.sqrt() работает с двойными числами в качестве входных параметров, поэтому вы не получите точных результатов для целых чисел большего размера чем 2 ^ 53 .

15
ответ дан 2 revs, 2 users 86% 23 December 2018 в 02:25
поделиться

Это должно быть намного быстрее для использования метод Newton для вычисления Целочисленный Квадратный корень , затем возвести в квадрат это число и проверку, как Вы делаете в своем текущем решении. Метод Newton является основанием для решения Carmack, упомянутого в некоторых других ответах. Необходимо быть в состоянии получить более быстрый ответ, так как Вы только интересуетесь целой частью корня, позволяя Вам остановить алгоритм аппроксимации раньше.

Другая оптимизация, которую можно попробовать: Если Цифровой Корень из числа не заканчивается в 1, 4, 7, или 9, число не полный квадрат. Это может использоваться в качестве быстрого способа устранить 60% Ваших исходных данных прежде, чем применить более медленный алгоритм квадратного корня.

16
ответ дан Bill the Lizard 23 December 2018 в 02:25
поделиться

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

10
ответ дан Hugh Allen 23 December 2018 в 02:25
поделиться

Для производительности вам очень часто приходится идти на некоторые компромиссы. Другие выразили различные методы, однако вы заметили, что хак Кармака был быстрее до определенных значений N. Затем вы должны проверить «n», и если оно меньше, чем число N, используйте хак Кармака, иначе используйте какой-то другой описанный метод в ответах здесь.

9
ответ дан BobbyShaftoe 23 December 2018 в 02:25
поделиться

Я провел собственный анализ нескольких алгоритмов в этой теме и получил некоторые новые результаты. Вы можете увидеть эти старые результаты в истории редактирования этого ответа, но они не точные, так как я допустил ошибку и потратил время на анализ нескольких алгоритмов, которые не являются близкими. Однако, извлекая уроки из нескольких разных ответов, у меня теперь есть два алгоритма, которые сокрушают «победителя» этой темы. Вот основная вещь, которую я делаю по-другому, чем все остальные:

// 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 , Тем не менее, это может добавить к времени выполнения, если многие из протестированных чисел имеют значительную степень двух факторов.

Ниже приведены алгоритмы:

  • Интернет - опубликованный ответ Кипа
  • Дуррон - Мой модифицированный ответ с использованием одного -проходный ответ в качестве основы
  • DurronTwo - Мой модифицированный ответ с использованием двухпроходного ответа (@JohnnyHeggheim) с некоторыми другими небольшими изменениями.

Вот примерное время выполнения, если числа генерируются с использованием 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
23
ответ дан durron597 23 December 2018 в 02:25
поделиться

Просто для протокола, другой подход заключается в использовании простого разложения. Если каждый фактор разложения четный, то число является идеальным квадратом. Итак, вы хотите посмотреть, можно ли разложить число как произведение квадратов простых чисел. Конечно, вам не нужно получать такое разложение, просто чтобы увидеть, существует ли оно.

Сначала создайте таблицу квадратов простых чисел, которые меньше, чем 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 в своем ответе.

12
ответ дан Cyrille Ka 23 December 2018 в 02:25
поделиться

Необходимо избавиться от части с 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% чисел.

7
ответ дан David Lehavi 23 December 2018 в 02:25
поделиться

Относительно метода Carmac кажется, что было бы довольно легко только выполнить итерации еще раз, который должен удвоить количество цифр точности. Это - в конце концов, чрезвычайно усеченный повторяющийся метод - Ньютон с очень хорошим первым предположением.

Относительно Вашего лучшего тока, я вижу две микрооптимизации:

  • перемещают проверку по сравнению с 0 после того, как проверка с помощью mod255
  • перестроит деление полномочия четыре для пропуска всех проверок на обычный (75%-й) случай.

Т.е.:

// 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;

, Очевидно, будет интересно знать, сколько чисел отобрано в каждой контрольной точке - я скорее сомневаюсь, что проверки действительно независимы, который делает вещи хитрыми.

0
ответ дан Ben 23 December 2018 в 12:25
поделиться
  • 1
    Спасибо, и также полезная ссылка. I' ve выбрал ответ с более общим решением. Аплодисменты. – Ludo 24 March 2011 в 10:41

Вызов 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 должен понять этот материал довольно быстро, таким образом, я не обвиняю их в этом.

6
ответ дан hydrodog 23 December 2018 в 12:25
поделиться
  • 1
    Могли Вы включать образец выхода с помощью user' s данные в качестве примера - это улучшит Ваш ответ. – DWright 24 December 2012 в 18:39

Euler проекта упоминается в тегах, и многие проблемы в нем требуют чисел проверки>> 2^64. Большая часть упомянутой выше оптимизации не работает легко, когда Вы работаете с 80-байтовым буфером.

Я использовал java BigInteger и немного измененную версию метода Newton, тот, который работает лучше с целыми числами. Проблема состояла в том что точные квадраты n^2 сходившийся к (n-1) вместо n потому что n^2-1 = (n-1)(n+1) и конечная погрешность была всего одним шагом ниже заключительного делителя и завершенного алгоритма. Было легко зафиксировать путем добавления одного к исходному аргументу прежде, чем вычислить ошибку. (Добавьте два для кубических корней, и т.д.),

Один хороший атрибут этого алгоритма - то, что можно сразу сказать, является ли число полным квадратом - конечная погрешность (не исправление) в методе Newton будет нулем. Простая модификация также позволяет Вам быстро вычислить floor(sqrt(x)) вместо самого близкого целого числа. Это удобно с несколькими Euler проблемами.

7
ответ дан 19 December 2019 в 20:14
поделиться

Это самая быстрая реализация Java, которую я мог придумать, используя комбинацию методов, предложенных другими в этом потоке.

  • Тест Mod-256
  • Неточный тест mod-3465 (избегает целочисленного деления за счет некоторых ложных срабатываний)
  • Квадратный корень с плавающей запятой, округление и сравнение с входным значением

Я также экспериментировал с этим изменения, но они не улучшили производительность:

  • Дополнительный тест mod-255
  • Деление входного значения на степени 4
  • Быстрый обратный квадратный корень (для работы с высокими значениями N требуется 3 итерации, достаточно, чтобы это медленнее, чем аппаратная функция извлечения квадратного корня.)

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;
        }
    }

}
8
ответ дан 19 December 2019 в 20:14
поделиться

"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.

0
ответ дан 19 December 2019 в 20:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: