यह निर्धारित करने का सबसे तेज़ तरीका है कि पूर्णांक का वर्गमूल एक पूर्णांक है या नहीं


1453

मैं यह निर्धारित करने के लिए सबसे तेज़ तरीका ढूंढ रहा हूं कि क्या कोई longमान पूर्ण वर्ग है (अर्थात इसका वर्गमूल एक और पूर्णांक है)

  1. मैंने इसे आसान तरीके से किया है, बिल्ट-इन Math.sqrt() फ़ंक्शन का उपयोग करके , लेकिन मैं सोच रहा हूं कि क्या पूर्णांक-केवल डोमेन तक खुद को सीमित करके इसे तेजी से करने का कोई तरीका है।
  2. लुकअप टेबल बनाए रखना अव्यावहारिक है (क्योंकि लगभग 2 31.5 पूर्णांक हैं जिनका वर्ग 2 63 से कम है )।

यहाँ बहुत ही सरल और सीधा तरीका है जो मैं अभी कर रहा हूँ:

public final static boolean isPerfectSquare(long n)
{
  if (n < 0)
    return false;

  long tst = (long)(Math.sqrt(n) + 0.5);
  return tst*tst == n;
}

नोट: मैं कई प्रोजेक्ट यूलर समस्याओं में इस फ़ंक्शन का उपयोग कर रहा हूं । इसलिए किसी और को इस कोड को कभी नहीं रखना होगा। और इस तरह के माइक्रो-ऑप्टिमाइज़ेशन से वास्तव में फर्क पड़ सकता है, क्योंकि चुनौती का हिस्सा हर एल्गोरिदम को एक मिनट से भी कम समय में करना है, और इस फ़ंक्शन को कुछ समस्याओं में लाखों बार कॉल करने की आवश्यकता होगी।


मैंने समस्या के विभिन्न समाधानों की कोशिश की है:

  • संपूर्ण परीक्षण के बाद, मैंने पाया कि 0.5Math.sqrt () के परिणाम में जोड़ना आवश्यक नहीं है, कम से कम मेरी मशीन पर नहीं।
  • तेजी से वर्गमूल उलटा तेजी से गया था, लेकिन इसके लिए गलत परिणाम दे दी है n> = 410881. लेकिन, जैसा कि ने सुझाव दिया BobbyShaftoe , हम n <410,881 के लिए FISR हैक उपयोग कर सकते हैं।
  • न्यूटन की विधि की तुलना में एक अच्छा सा धीमा था Math.sqrt()। यह शायद इसलिए है क्योंकि Math.sqrt()न्यूटन की विधि के समान कुछ का उपयोग करता है, लेकिन हार्डवेयर में लागू किया गया है इसलिए यह जावा की तुलना में बहुत तेज है। इसके अलावा, न्यूटन की विधि अभी भी युगल के उपयोग की आवश्यकता है।
  • एक संशोधित न्यूटन की विधि, जिसमें कुछ तरकीबों का इस्तेमाल किया गया था ताकि केवल पूर्णांक गणित शामिल हो, अतिप्रवाह से बचने के लिए कुछ हैक्स की आवश्यकता थी (मैं चाहता हूं कि यह फ़ंक्शन सभी सकारात्मक 64-बिट हस्ताक्षरित पूर्णांक के साथ काम करे), और यह अभी भी धीमी थी Math.sqrt()
  • बाइनरी चॉप भी धीमी थी। यह समझ में आता है क्योंकि बाइनरी चॉप 64-बिट संख्या के वर्गमूल को खोजने के लिए औसतन 16 पास की आवश्यकता होगी।
  • जॉन के परीक्षण के अनुसार, का उपयोग करते हुए orबयान सी में तेजी ++ एक का उपयोग करने से है switch, लेकिन में जावा और सी # वहाँ के बीच कोई अंतर हो गया लगता है orऔर switch
  • मैंने लुकअप टेबल (64 बूलियन मानों के निजी स्थिर सरणी के रूप में) बनाने की भी कोशिश की। तब या तो स्विच या orबयान के बजाय , मैं बस कहूंगा if(lookup[(int)(n&0x3F)]) { test } else return false;। मेरे आश्चर्य करने के लिए, यह (बस थोड़ा) धीमा था। ऐसा इसलिए है क्योंकि जावा में सरणी सीमा की जाँच की जाती है

21
यह जावा कोड है, जहां int == 32 बिट्स और लंबे == 64 बिट्स, और दोनों पर हस्ताक्षर किए जाते हैं।
किप

14
@ श्रीवस्तु: मैंने बड़े मूल्यों (2 ^ 53 से अधिक) पर कुछ परीक्षण किए हैं, और आपकी विधि कुछ झूठी सकारात्मकता देती है। पहला सामना किया गया n = 9007199326062755 के लिए, जो एक पूर्ण वर्ग नहीं है, लेकिन एक के रूप में वापस आ गया है।
किप

37
कृपया इसे "जॉन कार्मैक हैक" न कहें। वह इसके साथ नहीं आया था।
user9282

84
@ मम्मा - शायद, लेकिन इसका श्रेय उन्हें ही जाता है। हेनरी फोर्ड ने कार का आविष्कार नहीं किया, राइट ब्रदर्स ने हवाई जहाज का आविष्कार नहीं किया, और गैलीलियो ने सूरज के चारों ओर पृथ्वी की परिक्रमा करने वाले पहले व्यक्ति नहीं थे ... दुनिया चोरी के आविष्कारों से बनी है (और प्रेम)।
रॉबर्ट फ्रेजर

4
((1<<(n&15))|65004) != 0तीन अलग-अलग जांचों के बजाय, आप कुछ का उपयोग करके 'क्विकफ़ाइल' में एक छोटी गति बढ़ा सकते हैं ।
नबं नं

जवाबों:


735

मैंने एक विधि निकाली जो ~ 6बिट्स + कार्मैक + स्क्वैर कोड की तुलना में ~ 35% तेज काम करती है, कम से कम मेरे सीपीयू (x86) और प्रोग्रामिंग भाषा (C / C ++) के साथ। आपके परिणाम अलग-अलग हो सकते हैं, खासकर क्योंकि मुझे नहीं पता कि जावा फैक्टर कैसे चलेगा।

मेरा दृष्टिकोण तीन गुना है:

  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 है। क्योंकि यह तीन अलग-अलग अपराधों का एक उत्पाद है, केवल अवशेष mod 255 के लगभग 1/8 वर्ग हैं। हालांकि, मेरे अनुभव में, मोडुलो ऑपरेटर (%) को कॉल करने से लाभ प्राप्त करने की तुलना में अधिक लागत आती है, इसलिए मैं अवशेषों की गणना करने के लिए 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 मॉड 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 है। अंत में, आर और टी / 2-आर एक्स मोडुलो टी / 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 पुनरावृत्तियों के बराबर), पहले लूप से बाहर निकलना और कुछ टी मानों को छोड़ देना। पिछले भाग के लिए, मैं देखता हूं z = r - x * x, और बिट ट्रिक के साथ 2 विभाजन z की सबसे बड़ी शक्ति होने के लिए सेट करता हूं । यह मुझे टी मान को छोड़ने की अनुमति देता है जो कि आर के मूल्य को वैसे भी प्रभावित नहीं करेगा। मेरे मामले में प्रीकम्प्यूटेड स्टार्ट वैल्यू "सबसे छोटी पॉज़िटिव" स्क्वायर रूट मोडुलो 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;
}

5
वाह! मैं इसे जावा में बदलने और तुलना करने की कोशिश करूँगा, साथ ही परिणामों पर एक सटीकता की जांच करूँगा। मैं आपको बता दूंगा कि मुझे क्या मिला।
किप

79
वाह, यह सुंदर है। मैंने पहले हेंसल को उठाते हुए देखा था (बहुपद की मॉड्यूलेशन की जड़ें एक प्राइम की गणना) लेकिन मुझे यह भी पता नहीं था कि लेम्मा को संख्याओं के वर्गमूलों की गणना के लिए सावधानी से कम किया जा सकता है; यह है ... उत्थान :)
श्रीवत्सआर

3
@ नाइटक्रैकर यह नहीं है। 9 < 0 => false, 9&2 => 0, 9&7 == 5 => false, 9&11 == 8 => false
प्रिमो

53
Maartinus ने थोड़ी देर बाद 2x तेजी से समाधान (और बहुत कम) पोस्ट किया , जो कि बहुत अधिक प्यार नहीं लगता है।
जेसन सी

3
ऐसा लगता है कि अलग-अलग समाधानों में बहुत अधिक गति लाभ स्पष्ट वर्गों को छानने से प्राप्त होता है। क्या किसी ने Maartinus के समाधान के माध्यम से फ़िल्टरिंग की स्थिति को बेंचमार्क किया और फिर sqrt फ़ंक्शन का उपयोग किया जो कि एक अंतर्निहित फ़ंक्शन है?
user1914292

376

मुझे पार्टी में बहुत देर हो चुकी है, लेकिन मुझे बेहतर उत्तर देने की उम्मीद है; छोटा (मान लेना कि मेरा बेंचमार्क सही है) भी बहुत तेज है

long goodMask; // 0xC840C04048404040 computed below
{
    for (int i=0; i<64; ++i) goodMask |= Long.MIN_VALUE >>> (i*i);
}

public boolean isSquare(long x) {
    // This tests if the 6 least significant bits are right.
    // Moving the to be tested bit to the highest position saves us masking.
    if (goodMask << x >= 0) return false;
    final int numberOfTrailingZeros = Long.numberOfTrailingZeros(x);
    // Each square ends with an even number of zeros.
    if ((numberOfTrailingZeros & 1) != 0) return false;
    x >>= numberOfTrailingZeros;
    // Now x is either 0 or odd.
    // In binary each odd square ends with 001.
    // Postpone the sign test until now; handle zero in the branch.
    if ((x&7) != 1 | x <= 0) return x == 0;
    // Do it in the classical way.
    // The correctness is not trivial as the conversion from long to double is lossy!
    final long tst = (long) Math.sqrt(x);
    return tst * tst == x;
}

पहला परीक्षण अधिकांश गैर-वर्गों को जल्दी पकड़ता है। यह एक लंबे समय से पैक 64-आइटम तालिका का उपयोग करता है, इसलिए कोई सरणी एक्सेस लागत (अप्रत्यक्ष और सीमा चेक) नहीं है। समान रूप से यादृच्छिक के लिए long, यहां समाप्त होने की 81.25% संभावना है।

दूसरा परीक्षण सभी संख्याओं को उनके गुणन में विषम संख्या में पकड़ता है। यह विधि Long.numberOfTrailingZerosबहुत तेज़ है क्योंकि यह JIT-ed को एकल i86 निर्देश में प्राप्त करता है।

अनुगामी शून्य को छोड़ने के बाद, तीसरा परीक्षण बाइनरी में 011, 101, या 111 के साथ समाप्त होने वाली संख्याओं को संभालता है, जो कि कोई पूर्ण वर्ग नहीं हैं। यह नकारात्मक संख्याओं की भी परवाह करता है और 0 को भी संभालता है।

अंतिम परीक्षा doubleअंकगणित में वापस आती है । के रूप में doubleकेवल 53 बिट्स अपूर्णांश, से रूपांतरण है longकरने के लिए doubleबड़ा मूल्यों के लिए गोलाई भी शामिल है। बहरहाल, परीक्षण सही है (जब तक कि सबूत गलत नहीं है)।

Mod255 विचार को शामिल करने की कोशिश सफल नहीं रही।


3
शिफ्ट वैल्यू का यह निहित अर्थ है ... बुराई। क्या आपके पास जावा कल्पना में ऐसा कोई विचार है?
1

5
@ मुझे पता है कि इसके दो कारण हैं: 1. अधिक शिफ्टिंग से कोई मतलब नहीं है। 2. यह एचडब्ल्यू के कामों की तरह है और बिटवाइज़ ऑपरेशंस का उपयोग करने वाला कोई भी व्यक्ति प्रदर्शन में रुचि रखता है, इसलिए कुछ भी करना गलत होगा। -goodMask परीक्षण यह होता है, लेकिन यह यह होता है से पहले सही पारी। तो आपको इसे दोहराना होगा, लेकिन इस तरह यह सरल है और AFAIK एक छोटा सा तेज और समान रूप से अच्छा है।
मौर्टिनस

2
@dfeuer बेंचमार्क के लिए ASAP को जवाब देना महत्वपूर्ण है, और पीछे चल रही शून्य गणना स्वयं कोई जवाब नहीं देती है; यह सिर्फ एक प्रारंभिक कदम है। i86 / amd64 करते हैं। मोबाइलों में छोटे CPU के बारे में कोई विचार नहीं है, लेकिन सबसे खराब रूप से, जावा को उनके लिए एक AND निर्देश उत्पन्न करना है, जो निश्चित रूप से दूसरे तरीके के दौर की तुलना में सरल है।
Maaartinus

2
@ सेबैस्टियन ए शायद बेहतर परीक्षा if ((x & (7 | Integer.MIN_VALUE)) != 1) return x == 0;:।
Maaartinus 16

4
"जैसा कि डबल में केवल 56 बिट्स मंटिसा है" -> मैं कहूंगा कि इसकी अधिक संभावना 53 बिट है। इसके अलावा
chux -

132

आपको कुछ बेंचमार्किंग करनी होगी। सबसे अच्छा एल्गोरिथ्म आपके इनपुट के वितरण पर निर्भर करेगा।

आपका एल्गोरिथ्म लगभग इष्टतम हो सकता है, लेकिन आप अपने वर्गमूल रूटीन को कॉल करने से पहले कुछ संभावनाओं को बाहर करने के लिए एक त्वरित जांच करना चाह सकते हैं। उदाहरण के लिए, एक बिट-वार करके "हेक्स में अपने नंबर के अंतिम अंक को देखें।" परफेक्ट स्क्वॉयर केवल बेस 16 में 0, 1, 4, या 9 में समाप्त हो सकते हैं, इसलिए अपने इनपुट के 75% (यह मानते हुए कि वे समान रूप से वितरित किए गए हैं) के लिए आप कुछ बहुत तेज़ बिट ट्विडलिंग के बदले स्क्वायर रूट पर कॉल से बच सकते हैं।

किप ने हेक्स ट्रिक को लागू करते हुए निम्नलिखित कोड को बेंचमार्क किया। 100,000,000 के माध्यम से संख्या 1 का परीक्षण करते समय, यह कोड मूल रूप से दो बार तेजी से चला।

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 ++ में अनुरूप कोड का परीक्षण किया, तो यह वास्तव में मूल की तुलना में धीमा चला। हालांकि, जब मैंने स्विच स्टेटमेंट को समाप्त कर दिया, तो हेक्स ट्रिक ने एक बार फिर से कोड को दो बार तेज बना दिया।

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

स्विच स्टेटमेंट को खत्म करने का C # कोड पर बहुत कम प्रभाव पड़ा।


वह बहुत चालाक है ... उस के बारे में सोचा नहीं होगा
वॉरेन

अनुगामी बिट्स के बारे में अच्छी बात है। मैं यहाँ कुछ अन्य टिप्पणियों के साथ उस परीक्षण को संयोजित करने का प्रयास करूंगा।
पीटरअलेनवेब

3
शानदार समाधान। आश्चर्य है कि आप इसके साथ कैसे आए? क्या एक काफी स्थापित सिद्धांत या कुछ ऐसा है जिसे आपने समझ लिया है? : डी
जील शाह

3
@ लार्स को 0.5 जोड़ने की आवश्यकता नहीं है, प्रमाण के लिए लिंक के लिए मेरा समाधान देखें।
मआर्टिनस

2
@ जेरी गोयल यह संकलक और मामलों के मूल्यों पर निर्भर करता है। एक परिपूर्ण कंपाइलर में, एक स्विच हमेशा कम से कम उतना ही तेज़ होता है जितना कि और-और। लेकिन कंपाइलर परफेक्ट नहीं हैं, इसलिए जॉन ने जैसा किया, उसे आज़माना सबसे अच्छा है।
फिशिनियर

52

मैं उस भयानक समय के बारे में सोच रहा था जो मैंने न्यूमेरिकल एनालिसिस कोर्स में बिताया है।

और फिर मुझे याद है, इस समारोह में 'क्वेक सोर्स कोड से नेट' के आसपास चक्कर लगा रहा था:

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

जो मूल रूप से एक वर्गमूल की गणना करता है, न्यूटन के सन्निकटन समारोह (सटीक नाम याद रखना) का उपयोग करके।

यह प्रयोग करने योग्य होना चाहिए और इससे भी तेज हो सकता है, यह अभूतपूर्व आईडी सॉफ्टवेयर के खेल में से एक है!

यह C ++ में लिखा गया है, लेकिन विचार प्राप्त करने के बाद जावा में उसी तकनीक का पुन: उपयोग करना बहुत कठिन नहीं होना चाहिए:

मैंने इसे मूल रूप से पाया: http://www.codemaestro.com/reviews/9

न्यूटन की विधि विकिपीडिया पर बताई गई: http://en.wikipedia.org/wiki/Newton%27s_method

यह कैसे काम करता है, इसकी अधिक व्याख्या के लिए आप लिंक का अनुसरण कर सकते हैं, लेकिन यदि आप बहुत परवाह नहीं करते हैं, तो यह मोटे तौर पर मुझे ब्लॉग पढ़ने और संख्यात्मक विश्लेषण पाठ्यक्रम लेने से याद है:

  • * (long*) &yमूल रूप से एक तेजी से परिवर्तित करने के लिए लंबे समय तक समारोह तो पूर्णांक संचालन कच्चे बाइट्स पर लागू किया जा सकता है।
  • 0x5f3759df - (i >> 1);लाइन सन्निकटन समारोह के लिए एक पूर्व गणना की बीज मूल्य है।
  • * (float*) &iमान को वापस फ़्लोटिंग पॉइंट में परिवर्तित करता है।
  • y = y * ( threehalfs - ( x2 * y * y ) )लाइन bascially फिर से समारोह की मूल्य iterates।

सन्निकटन फ़ंक्शन अधिक सटीक मान देता है जितना अधिक आप परिणाम पर फ़ंक्शन को पुनरावृत्त करते हैं। क्वेक के मामले में, एक पुनरावृत्ति "काफी अच्छा" है, लेकिन अगर यह आपके लिए नहीं था ... तो आप अपनी आवश्यकता के अनुसार अधिक पुनरावृत्ति जोड़ सकते हैं।

यह तेज़ होना चाहिए क्योंकि यह भोले-भाले वर्ग रूटिंग में किए गए विभाजन कार्यों की संख्या को 2 से सरल विभाजन (वास्तव में एक * 0.5Fबहुक्रियात्मक संचालन) तक कम कर देता है और इसकी जगह कुछ निश्चित संख्या में गुणन क्रिया करता है।


9
यह ध्यान दिया जाना चाहिए कि यह 1 / sqrt (संख्या) देता है, sqrt (संख्या) नहीं। मैंने कुछ परीक्षण किया है, और यह n = 410881 पर शुरू होने में विफल रहता है: जॉन कार्मैक मैजिक फॉर्मूला 642.00104 पर लौटता है, जब वास्तविक वर्गमूल 641 होता है।
किप

11
आप तेजी से उलटा वर्ग जड़ों पर क्रिस लोमोन्स पेपर देख सकते हैं: lomont.org/Math/Papers/2003/InvSqrt.pdf यह यहाँ के रूप में एक ही तकनीक का उपयोग करता है, लेकिन एक अलग जादू संख्या के साथ। पेपर बताता है कि मैजिक नंबर क्यों चुना गया था।

4
इसके अलावा, परे 3 डी . com / content / articles / 8 और परे3d.com/content/articles/15 इस पद्धति की उत्पत्ति के रूप में कुछ प्रकाश डालते हैं । यह अक्सर जॉन कार्मैक को जिम्मेदार ठहराया जाता है, लेकिन ऐसा लगता है कि मूल कोड (संभवतः) गैरी ट्रॉली, ग्रेग वाल्श और शायद अन्य लोगों द्वारा लिखा गया था।

3
इसके अलावा, आप जावा में टाइप और फ्लोट नहीं कर सकते।
एंटीमनी

10
@Antimony कौन कहता है? FloatToIntBits और IntToFloatBits जावा 1.0.2 के बाद से आसपास हैं।
corsiKa

38

मुझे यकीन नहीं है कि यह तेज या सटीक होगा, लेकिन आप स्क्वायर को तेजी से हल करने के लिए जॉन कार्मैक के जादुई स्क्वायर रूट का उपयोग कर सकते हैं । आप शायद सभी संभावित 32 बिट पूर्णांकों के लिए आसानी से इसका परीक्षण कर सकते हैं, और पुष्टि कर सकते हैं कि आपको वास्तव में सही परिणाम मिला है, क्योंकि यह केवल एक मूल्यांकन है। हालाँकि, अब मुझे लगता है कि इसके बारे में, डबल्स का उपयोग करना भी अनुमान लगा रहा है, इसलिए मुझे यकीन नहीं है कि यह कैसे खेल में आएगा।


10
मेरा मानना ​​है कि कार्मैक की चाल इन दिनों काफी बेकार है। बिल्ट-इन sqrt इंस्ट्रक्शन एक बहुत तेज़ है जितना पहले इस्तेमाल किया गया था, इसलिए आप नियमित रूप से स्क्वायर रूट का प्रदर्शन करने और परीक्षण करने से बेहतर हो सकते हैं यदि परिणाम एक int है। हमेशा की तरह, इसे बेंचमार्क करें।
जलफ

4
यह टूटना शुरू होता है n = 410881 पर, जॉन कार्मैक मैजिक फॉर्मूला 642.00104 पर लौटता है, जब वास्तविक वर्गमूल 641 होता है।
किप

11
मैंने हाल ही में एक जावा गेम में कार्मैक की चाल का उपयोग किया और यह बहुत प्रभावी था, लगभग 40% का स्पीडअप दिया, इसलिए यह अभी भी उपयोगी है, कम से कम जावा में।
फिनवे

3
@ रोटर फ्रेजर हां + समग्र फ्रेम दर में 40%। (। जो मैं भी एक समान बिट twiddling हैक का उपयोग कर अनुकूलित किया था) खेल एक कण भौतिकी प्रणाली है जो लगभग सभी उपलब्ध CPU चक्र लिया, वर्गमूल समारोह और गोल करने के लिए निकटतम-पूर्णांक समारोह का प्रभुत्व था
finnw

5
लिंक टूट गया है।
Pixar

36

यदि आप "सही" वर्गमूल खोजने की कोशिश करने के लिए एक बाइनरी चॉप करते हैं, तो आप काफी आसानी से पता लगा सकते हैं कि आपको जो मूल्य मिला है वह बताने के लिए पर्याप्त है:

(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 : आप पास हैं, लेकिन यह सही नहीं है: गलत लौटें
  • 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 पुनरावृत्तियों को लेने वाला है।


मेरा पैसा इस तरह के दृष्टिकोण पर है। Sqrt () को कॉल करने से बचें क्योंकि यह पूर्ण वर्गमूल की गणना कर रहा है, और आपको केवल पहले कुछ अंकों की आवश्यकता है।
पीटरअलेनवेब

3
दूसरी ओर, यदि फ्लोटिंग पॉइंट एक समर्पित एफपी यूनिट में किया जा रहा है, तो यह सभी प्रकार के मजेदार ट्रिक्स का उपयोग कर सकता है। मैं एक बेंचमार्क के बिना इस पर दांव लगाना पसंद नहीं करूंगा :) (मैं आज रात इसे C # में आजमा सकता हूं, बस देखना है ...)
जॉन स्कीट

8
हार्डवेयर वर्ग वास्तव में इन दिनों बहुत तेज हैं।
एडम रोसेनफील्ड 3

24

मैंने इस थ्रेड में कई एल्गोरिदम का अपना विश्लेषण चलाया और कुछ नए परिणामों के साथ आया। आप इस उत्तर के संपादित इतिहास में उन पुराने परिणामों को देख सकते हैं, लेकिन वे सटीक नहीं हैं, क्योंकि मैंने एक गलती की है, और कई एल्गोरिदम का विश्लेषण करते हुए समय बर्बाद किया है जो करीब नहीं हैं। हालांकि, कई अलग-अलग उत्तरों से सबक खींचते हुए, मेरे पास अब दो एल्गोरिदम हैं जो इस धागे के "विजेता" को कुचलते हैं। यहां मुख्य बात यह है कि मैं हर किसी से अलग हूं:

// 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यदि कथन में कथन को बहुत सरल कर देती है । हालाँकि, यह रनटाइम में जोड़ सकता है यदि परीक्षण किए गए संख्याओं में से कई में महत्वपूर्ण शक्ति-दो कारक हैं।

नीचे दिए गए एल्गोरिदम इस प्रकार हैं:

  • इंटरनेट - किप का पोस्टेड उत्तर
  • Durron - आधार के रूप में वन-पास उत्तर का उपयोग करके मेरा संशोधित उत्तर
  • 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 कैलिपर 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, जो कि एक 3mb फ़ाइल के बारे में है (इस मामले में 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

3
एक विशाल देखने की मेज एक अच्छे विचार की तरह प्रतीत नहीं होती है। X86 हार्डवेयर sqrt अनुदेश (~ 20 चक्र) की तुलना में एक कैश मिस धीमा (~ 100 से 150 चक्र) है। थ्रूपुट-वार, आप बहुत सारे उत्कृष्ट कैश-मिस को बनाए रख सकते हैं, लेकिन आप अभी भी अन्य उपयोगी डेटा निकाल रहे हैं। एक विशाल लुकअप टेबल केवल इसके लायक होगा यदि यह किसी भी अन्य विकल्प की तुलना में तेजी से बहुत छोटा था, और यह फ़ंक्शन आपके पूरे कार्यक्रम के प्रदर्शन का प्रमुख कारक था।
पीटर कॉर्ड्स

1
@SwissFrank: परफेक्ट-स्क्वायर आपके प्रोग्राम की एकमात्र चीज़ की जाँच करता है? एक लुकअप तालिका एक माइक्रोबेंचमार्क में अच्छी दिख सकती है जो इसे एक तंग लूप में बार-बार कॉल करती है, लेकिन एक वास्तविक कार्यक्रम में जिसके काम के सेट में अन्य डेटा है, यह अच्छा नहीं है।
पीटर कॉर्ड्स

1
0x1FFFFFE बिट्स का एक बिटमैप एक पैक बिटमैप के रूप में संग्रहीत होने पर 4 मेगा- बाइट लेता है । आधुनिक इंटेल डेस्कटॉप पर एक L3 कैश हिट > विलंबता के 40 चक्र, और एक बड़े Xeon पर बदतर है; हार्डवेयर sqrt + mul विलंबता से अधिक लंबा। यदि 1 बाइट प्रति मान के साथ बाइट -मैप के रूप में संग्रहीत किया जाता है , तो यह लगभग 32 एमबी है; किसी भी चीज़ के L3 कैश से बड़ा लेकिन कई-कोर Xeon है जहाँ सभी कोर एक बहुत बड़ा कैश साझा करते हैं। इसलिए यदि आपके इनपुट डेटा में इनपुट की एक बड़ी श्रृंखला में एक समान यादृच्छिक वितरण है, तो आपको एक तंग लूप में भी L2 कैश की बहुत सारी छूट मिल जाएगी। (इंटेल पर निजी प्रति-कोर L2 केवल 256k है, ~ 12 चक्र विलंबता के साथ।)
पीटर कॉर्डेस

1
@SwissFrank: ओह, अगर आप सब कर रहे हैं रूट की जाँच, तो L3 हिट पाने के लिए एक बिटमैप के साथ इस की क्षमता है। मैं विलंबता को देख रहा था, लेकिन एक साथ कई मिसाइलें उड़ान में हो सकती हैं, इसलिए थ्रूपुट संभावित रूप से अच्छा है। OTOH, SIMD sqrtpsथ्रूपुट या यहां तक ​​कि sqrtpd(डबल-परिशुद्धता) Skylake पर बहुत बुरा नहीं है, लेकिन पुराने सीपीयू पर विलंबता से बहुत बेहतर नहीं है। वैसे भी 7-cpu.com/cpu/Haswell.html में कुछ अच्छे प्रयोगात्मक नंबर हैं, और अन्य सीपीयू के लिए पृष्ठ हैं। Agner Fog के माइक्रो गाइड गाइड pdf में Intel और AMD uarches के लिए कुछ कैश विलंबता संख्याएं हैं: agner.org/optimize
पीटर कॉर्ड

1
जावा से x86 SIMD का उपयोग करना एक समस्या है, और जब तक आप int-> fp और fp-> int रूपांतरण की लागत में जोड़ते हैं, यह प्रशंसनीय है कि एक बिटमैप बेहतर हो सकता है। double+ -2 ^ 24 रेंज (ताकि 32-बिट पूर्णांक उस के बाहर हो सकता है) के बाहर कुछ पूर्णांक को गोल करने से बचने के लिए आपको सटीकता की आवश्यकता होती है , और sqrtpdधीमी गति के sqrtpsसाथ-साथ केवल निर्देश के अनुसार कई तत्वों को आधा प्रसंस्करण करना होता है (प्रति SIMD वेक्टर) ।
पीटर कॉर्ड्स

18

इंटेगर स्क्वायर रूट की गणना करने के लिए न्यूटन की विधि का उपयोग करने के लिए यह बहुत तेज़ होना चाहिए , फिर इस नंबर को जांचें और जांचें, जैसा कि आप अपने वर्तमान समाधान में करते हैं। न्यूटन की विधि कुछ अन्य उत्तरों में वर्णित कार्मैक समाधान का आधार है। आपको एक तेज़ उत्तर प्राप्त करने में सक्षम होना चाहिए क्योंकि आप केवल रूट के पूर्णांक भाग में रुचि रखते हैं, जिससे आप अनुमानित एल्गोरिथ्म को जल्द ही रोक सकते हैं।

एक और अनुकूलन जिसे आप आज़मा सकते हैं: यदि किसी संख्या का डिजिटल रूट 1, 4, 7 या 9 में समाप्त नहीं होता है, तो संख्या एक पूर्ण वर्ग नहीं है। यह धीमी स्क्वायर रूट एल्गोरिथ्म को लागू करने से पहले अपने इनपुट के 60% को खत्म करने के लिए एक त्वरित तरीके के रूप में इस्तेमाल किया जा सकता है।


1
डिजिटल रूट पूरी तरह से कम्प्यूटेशनल रूप से मोडुलो के बराबर है, इसलिए यहां अन्य मोडुलो विधियों के साथ विचार किया जाना चाहिए, जैसे कि मॉड 16 और मॉड 255।
क्रिश्चियन ऑडर्ड

1
क्या आप सुनिश्चित हैं कि डिजिटल रूट modulo के बराबर है? यह लिंक द्वारा समझाया गया कुछ अलग सा लगता है। ध्यान दें कि सूची 1,4,7,9 नहीं 1,4,5,9 है।
फ्रैक्टली

1
दशमलव प्रणाली में डिजिटल रूट modulo 9 (अच्छी तरह से dr (n) = 1 + ((n-1-mod 9) का उपयोग करने के बराबर है; इसलिए थोड़ी सी शिफ्ट भी)। 0,1,4,5,9 संख्याएं मोडुलो 16 के लिए हैं, और 0, 1, 4, 7 मोडुलो 9 के लिए हैं - जो डिजिटल रूट के लिए 1, 4, 7, 9 के अनुरूप हैं।
हंस ओल्सन

16

मैं चाहता हूं कि यह फ़ंक्शन सभी सकारात्मक 64-बिट हस्ताक्षरित पूर्णांक के साथ काम करे

Math.sqrt()इनपुट मापदंडों के रूप में डबल्स के साथ काम करता है, इसलिए आपको पूर्णांक 2 ^ 53 से बड़े के लिए सटीक परिणाम नहीं मिलेंगे ।


5
मैंने वास्तव में 2 ^ 53 से बड़े सभी पूर्ण वर्गों पर उत्तर का परीक्षण किया है, साथ ही प्रत्येक पूर्ण वर्ग के नीचे 5 से प्रत्येक पूर्ण वर्ग के ऊपर 5 तक की संख्या, और मुझे सही परिणाम मिलता है। (राउंडऑफ़ त्रुटि तब ठीक की जाती है जब मैं एक लंबे, तब वर्ग को उस मूल्य और तुलना में उत्तर देता हूं)
किप

2
@ किप: मुझे लगता है कि मैंने साबित कर दिया है कि यह काम करता है
Maaartinus

परिणाम पूरी तरह से सही नहीं हैं, लेकिन जितना आप सोच सकते हैं उससे अधिक सटीक। यदि हम रूपांतरण के बाद कम से कम 15 सटीक अंकों को दोहराते हैं और वर्गमूल के बाद, तो यह बहुत है, क्योंकि हमें 32 बिट वर्ग मूल के लिए 11: 10 अंकों की आवश्यकता नहीं है और दशमलव स्थान के लिए 1 से कम है, क्योंकि +0.5 निकटतम करने के लिए गोल।
mwfearnley

3
Math.sqrt () पूरी तरह से सही नहीं है, लेकिन यह करने के लिए नहीं है। पहले पोस्ट में tst sqrt (N) के समीप एक पूर्णांक है। यदि N एक वर्ग नहीं है, तो tst * tst! = N, tst का मान चाहे जो भी हो। यदि N एक पूर्ण वर्ग है, तो sqrt (N) <2 ^ 32, और जब तक sqrt (N) की गणना एक त्रुटि <0.5 के साथ की जाती है, हम ठीक हैं।
gnasher729

13

सिर्फ रिकॉर्ड के लिए, एक और दृष्टिकोण प्रधान अपघटन का उपयोग करना है। यदि अपघटन का प्रत्येक कारक सम है, तो संख्या एक पूर्ण वर्ग है। तो आप क्या चाहते हैं यह देखने के लिए कि क्या संख्याओं को अभाज्य संख्याओं के वर्ग के उत्पाद के रूप में विघटित किया जा सकता है। बेशक, आपको इस तरह के अपघटन प्राप्त करने की आवश्यकता नहीं है, बस यह देखने के लिए कि क्या यह मौजूद है।

सबसे पहले अभाज्य संख्याओं के वर्ग का निर्माण करें जो 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 कहता है।


1
पूर्णांक विभाजन वर्तमान हार्डवेयर पर FP sqrt से धीमा है। इस विचार का कोई मौका नहीं है। >। <2008 में भी, Core2 का sqrtsdथ्रूपुट 6-58c प्रति एक है। इसका idivप्रति 12-36 चक्र एक है। (थ्रूपुट के समान विलंबताएं: न तो इकाई पाइपलाइन की गई है)।
पीटर कॉर्ड्स

sqrt को पूरी तरह से सटीक होने की आवश्यकता नहीं है। इसलिए आप पूर्णांक-स्क्वेअर द्वारा परिणाम की जांच करते हैं और यह पूर्णांक की तुलना करते हुए तय करते हैं कि इनपुट पूर्णांक में सटीक पूर्णांक sqrt था या नहीं।
पीटर कॉर्ड्स

11

यह बताया गया है कि dएक पूर्ण वर्ग के अंतिम अंक केवल कुछ मानों को ले सकते हैं। किसी dसंख्या का अंतिम अंक (आधार में b) nशेष के समान होता nहै bd, जब वह विभाजित होता है , अर्थात। C संकेतन में n % pow(b, d)

यह किसी भी मापांक mयानि सामान्यीकृत किया जा सकता है । n % mकुछ वर्गों को पूर्ण वर्गों से बाहर निकालने के लिए उपयोग किया जा सकता है। आपके द्वारा वर्तमान में उपयोग किया जा रहा मापांक 64 है, जो 12 को अनुमति देता है, अर्थात। 19% अवशेष, संभव चौकों के रूप में। थोड़ा कोडिंग के साथ मैंने मापांक 110880 पाया, जो केवल 2016 को अनुमति देता है, अर्थात। संभव चौकों के रूप में 1.8% अवशेष। इसलिए एक मापांक ऑपरेशन (यानी विभाजन) और आपकी मशीन पर एक वर्गमूल बनाम एक टेबल लुकअप की लागत के आधार पर, इस मापांक का उपयोग तेज हो सकता है।

वैसे यदि जावा में लुकअप टेबल के लिए बिट्स के एक पैक किए गए सरणी को स्टोर करने का एक तरीका है, तो इसका उपयोग न करें। 110880 32-बिट शब्द इन दिनों बहुत अधिक रैम नहीं है और मशीन शब्द प्राप्त करना एक बिट को लाने की तुलना में तेजी से होने जा रहा है।


अच्छा लगा। क्या आपने इसे बीजगणित या परीक्षण और त्रुटि के आधार पर काम किया? मैं देख सकता हूँ कि यह इतना प्रभावी क्यों है - पूर्ण वर्गों के बीच बहुत सी टक्कर, जैसे 333 ^ 2% 110880 == 3 ^ 2, 334 ^ 2% 110880 == 26 ^ 2, 338 ^ 2% 110880 = 58 ^ 2 .. ।
फाइननव

IIRC यह क्रूर बल था, लेकिन ध्यान दें कि 110880 = 2 ^ 5 * 3 ^ 2 * 5 * 7 * 11, जो 6 * 3 * 2 * 2 * 2 - 1 = 143 उचित विभाजक देता है।
ह्यूज एलन

मैंने पाया कि लुकअप की सीमाओं के कारण, ४४३५२ बेहतर काम करता है, २.६% पास दर के साथ। कम से कम मेरे कार्यान्वयन में।
फ्रैक्चरल

1
वर्तमान x86 हार्डवेयर पर idivFP sqrt ( sqrtsd) की लागत में इंटेगर डिवीजन ( ) बराबर या बदतर है । इसके अलावा, बिटफिल्ड से बचने के लिए पूरी तरह से असहमत हैं। एक बिटफील्ड के साथ कैश हिट रेट बेहतर होगा, और एक बिटफील्ड में एक बिट का परीक्षण केवल एक या दो से अधिक सरल निर्देश पूरे बाइट का परीक्षण करने से होता है। (नॉन-बिटफिल्ड के रूप में भी कैश में फिट होने वाली छोटी तालिकाओं के लिए, एक बाइट सरणी सबसे अच्छा होगा, 32 बिट इनट्स नहीं। x86 में 32 बिट डॉर्ड के बराबर गति के साथ सिंगल-बाइट एक्सेस है।)
पीटर कॉर्ड्स

11

एक पूर्णांक समस्या एक पूर्णांक समाधान के योग्य है। इस प्रकार

(गैर-ऋणात्मक) पूर्णांकों पर द्विआधारी खोज करते हैं ताकि सबसे बड़ा पूर्णांक ऐसा हो t**2 <= n। फिर परीक्षण करें कि क्या r**2 = nवास्तव में। इसमें O (लॉग एन) समय लगता है।

अगर आपको पता नहीं है कि बाइनरी पॉजिटिव पूर्णांकों की खोज कैसे करें क्योंकि सेट अनबाउंड है, तो यह आसान है। आप f(t) = t**2 - nदो की शक्तियों पर अपने बढ़ते फ़ंक्शन f (ऊपर ) की गणना करके शुरू करते हैं । जब आप इसे सकारात्मक देखते हैं, तो आपको एक ऊपरी सीमा मिल जाती है। तब आप मानक बाइनरी खोज कर सकते हैं।


वास्तव में समय कम से कम होगा O((log n)^2)क्योंकि गुणा निरंतर-समय नहीं है, लेकिन वास्तव में इसकी एक निचली सीमा होती है O(log n), जो बड़ी बहु-सटीक संख्याओं के साथ काम करते समय स्पष्ट हो जाती है। लेकिन इस विकी का दायरा 64-बिट लगता है, इसलिए शायद यह nbd है।

10

Maaartinus के समाधान का निम्नलिखित सरलीकरण रनटाइम से कुछ प्रतिशत अंक दाढ़ी करता प्रतीत होता है, लेकिन मैं बेंचमार्किंग के लिए बेंचमार्किंग में इतना अच्छा नहीं हूं कि मैं विश्वास कर सकूं:

long goodMask; // 0xC840C04048404040 computed below
{
    for (int i=0; i<64; ++i) goodMask |= Long.MIN_VALUE >>> (i*i);
}

public boolean isSquare(long x) {
    // This tests if the 6 least significant bits are right.
    // Moving the to be tested bit to the highest position saves us masking.
    if (goodMask << x >= 0) return false;
    // Remove an even number of trailing zeros, leaving at most one.
    x >>= (Long.numberOfTrailingZeros(x) & (-2);
    // Repeat the test on the 6 least significant remaining bits.
    if (goodMask << x >= 0 | x <= 0) return x == 0;
    // Do it in the classical way.
    // The correctness is not trivial as the conversion from long to double is lossy!
    final long tst = (long) Math.sqrt(x);
    return tst * tst == x;
}

यह जाँच के लायक होगा कि पहला परीक्षण कैसे छोड़ा गया,

if (goodMask << x >= 0) return false;

प्रदर्शन को प्रभावित करेगा।


2
परिणाम यहाँ हैं । पहला परीक्षण हटाना बुरा है क्योंकि यह ज्यादातर मामलों को बहुत सस्ते में हल करता है। स्रोत मेरे उत्तर में है (अद्यतन)।
माॅर्टिनस

9

प्रदर्शन के लिए, आपको अक्सर कुछ समझौते करने पड़ते हैं। अन्य लोगों ने विभिन्न तरीकों को व्यक्त किया है, हालांकि, आपने नोट किया कि कार्मैक की हैक एन के कुछ मूल्यों तक तेज थी। फिर, आपको "एन" की जांच करनी चाहिए और यदि यह संख्या एन से कम है, तो कार्मैक के हैक का उपयोग करें, अन्यथा वर्णित कुछ अन्य विधि का उपयोग करें यहाँ जवाब में।


मैंने आपके सुझाव को समाधान में भी शामिल किया है। इसके अलावा, अच्छा संभाल। :)
किप

8

यह सबसे तेज़ जावा कार्यान्वयन है जो मैं इस धागे में दूसरों द्वारा सुझाई गई तकनीकों के संयोजन का उपयोग करके कर सकता हूं।

  • मॉड- 256 टेस्ट
  • Inexact mod-3465 परीक्षण (कुछ झूठी सकारात्मक की कीमत पर पूर्णांक विभाजन से बचा जाता है)
  • फ्लोटिंग-पॉइंट स्क्वायर रूट, इनपुट मूल्य के साथ गोल और तुलना करें

मैंने इन संशोधनों के साथ भी प्रयोग किया लेकिन उन्होंने प्रदर्शन में मदद नहीं की:

  • अतिरिक्त mod-255 परीक्षण
  • 4 की शक्तियों द्वारा इनपुट मूल्य को विभाजित करना
  • तेजी से उलटा वर्गमूल (एन के उच्च मूल्यों के लिए काम करने के लिए इसे 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;
        }
    }

}

7

आपको शुरू से ही एन राइट के 2-पॉवर वाले हिस्से से छुटकारा पाना चाहिए।

2 संपादित करें नीचे के लिए जादुई अभिव्यक्ति होनी चाहिए

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;

पहला संपादन:

मामूली सुधार:

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

प्रोजेक्ट यूलर को टैग्स में वर्णित किया गया है और इसमें कई समस्याओं के लिए चेक नंबर की आवश्यकता है >> 2^64। जब आप 80 बाइट बफर के साथ काम कर रहे हैं, तो ऊपर उल्लिखित अधिकांश अनुकूलन आसानी से काम नहीं करते हैं।

मैंने java BigInteger का उपयोग किया और न्यूटन की विधि का थोड़ा संशोधित संस्करण, एक जो पूर्णांक के साथ बेहतर काम करता है। समस्या यह थी कि सटीक वर्गn^2 करने के लिए कन्वर्ज्ड (n-1)बजाय nक्योंकि n^2-1 = (n-1)(n+1)और अंतिम त्रुटि अंतिम भाजक नीचे बस एक कदम और कलन विधि समाप्त किया गया था। त्रुटि की गणना करने से पहले मूल तर्क में एक जोड़कर ठीक करना आसान था। (घन जड़ों के लिए दो जोड़ें, आदि)

इस एल्गोरिथ्म की एक अच्छी विशेषता यह है कि आप तुरंत बता सकते हैं कि क्या संख्या एक पूर्ण वर्ग है - न्यूटन की विधि में अंतिम त्रुटि (सुधार नहीं) शून्य होगी। एक साधारण संशोधन आपको floor(sqrt(x))निकटतम पूर्णांक के बजाय जल्दी से गणना करने देता है । यह कई यूलर समस्याओं के साथ काम करता है।


1
मैं इन एल्गोरिदम के बारे में एक ही बात सोच रहा था कि बहु-सटीक बफ़र्स के लिए अच्छी तरह से अनुवाद नहीं हो रहा है। तो सोचा कि मैं इसे यहाँ चिपका दूंगा ... मुझे वास्तव में विशाल संख्याओं के लिए बेहतर विषमतापूर्ण जटिलता के साथ एक संभाव्य वर्गीयता का परीक्षण मिला ..... जहां संख्या सिद्धांत अनुप्रयोग असामान्य रूप से खुद को नहीं पाते हैं। हालांकि प्रोजेक्ट यूलर से परिचित नहीं ... दिलचस्प लग रहा है।

6

यह पुराने मार्केंट कैलकुलेटर एल्गोरिथ्म के द्विआधारी के दशमलव से फिर से काम करता है (क्षमा करें, मेरे पास एक संदर्भ नहीं है), रूबी में, विशेष रूप से इस प्रश्न के लिए अनुकूलित:

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

यहाँ कुछ इसी तरह का एक कार्यपट्टी है (कृपया मुझे कोडिंग शैली / गंध या क्लंकी ओ / ओ के लिए वोट न करें - यह एल्गोरिथ्म है जो मायने रखता है, और सी ++ मेरी घरेलू भाषा नहीं है)। इस मामले में, हम अवशेषों की तलाश कर रहे हैं == 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;
};

पुनरावृत्तियों की संख्या O (ln n) दिखती है, जहाँ n v की बिट-लंबाई है, इसलिए मुझे संदेह है कि इससे बड़ी v के लिए बहुत बचत होगी। फ़्लोटिंग पॉइंट sqrt धीमा है, शायद 100-200 चक्र, लेकिन पूर्णांक गणित नहीं है या तो मुफ्त। 15 चक्रों के साथ एक दर्जन पुनरावृत्तियों, और यह एक धोने होगा। फिर भी, दिलचस्प होने के लिए +1।
ताड़मास

वास्तव में, मेरा मानना ​​है कि एक्सओआर द्वारा जोड़ और घटाव किया जा सकता है।
ब्रेंट.लॉन्गबरो

यह एक भद्दी टिप्पणी थी - केवल जोड़ एक एक्सओआर द्वारा किया जा सकता है; घटाव अंकगणित है।
ब्रेंट.लॉन्गबरो

1
क्या XOR के रन टाइम और इसके अलावा वास्तव में कोई ठोस अंतर है?
ताड़मास

1
@ तद्माः शायद "बाद में अनुकूलन" नियम को तोड़ने के लिए पर्याप्त नहीं है। (:-)
ब्रेंट.लॉन्गबरो

6

Sqrt कॉल पूरी तरह से सही नहीं है, जैसा कि उल्लेख किया गया है, लेकिन यह दिलचस्प और शिक्षाप्रद है कि यह गति के मामले में अन्य उत्तरों को नहीं उड़ाता है। आखिरकार, एक sqrt के लिए असेंबली भाषा निर्देशों का क्रम छोटा है। इंटेल में एक हार्डवेयर निर्देश है, जिसका उपयोग जावा द्वारा नहीं किया जाता है, मेरा मानना ​​है कि यह IEEE के अनुरूप नहीं है।

तो यह धीमा क्यों है? क्योंकि जावा वास्तव में जेएनआई के माध्यम से सी दिनचर्या कह रहा है, और यह वास्तव में एक जावा सबरूटीन को कॉल करने की तुलना में धीमा है, जो स्वयं इनलाइन करने की तुलना में धीमा है। यह बहुत कष्टप्रद है, और यदि आवश्यक हो, तो फ्लोटिंग पॉइंट लाइब्रेरी कॉल में जावा को एक बेहतर समाधान के साथ आना चाहिए। ओह अच्छा।

C ++ में, मुझे संदेह है कि सभी जटिल विकल्प गति से हार जाएंगे, लेकिन मैंने उन सभी की जांच नहीं की है। मैंने क्या किया, और जावा लोग क्या उपयोगी पाएंगे, एक साधारण हैक है, ए रेक्स द्वारा सुझाए गए विशेष केस परीक्षण का विस्तार है। एक एकल सरणी के रूप में एक लंबे समय के मूल्य का उपयोग करें, जो की जाँच की सीमा नहीं है। इस तरह, आपके पास 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;
}

दिनचर्या .PerfectSquare5 मेरी कोर 2 डुओ मशीन पर लगभग 1/3 समय है। मुझे संदेह है कि उसी तर्ज पर आगे की टहनियां औसतन समय को और कम कर सकती हैं, लेकिन हर बार जब आप जांच करते हैं, तो आप अधिक नष्ट करने के लिए अधिक परीक्षण बंद कर रहे हैं, इसलिए आप उस सड़क पर बहुत दूर नहीं जा सकते।

निश्चित रूप से, नकारात्मक के लिए एक अलग परीक्षण करने के बजाय, आप उसी तरह से उच्च 6 बिट्स की जांच कर सकते हैं।

ध्यान दें कि मैं जो कुछ भी कर रहा हूं वह सभी संभावित वर्गों को खत्म कर रहा है, लेकिन जब मेरे पास एक संभावित मामला है तो मुझे मूल, इनबिल्ट isPerfectSquare को कॉल करना होगा।

Init2 दिनचर्या को pp1 और pp2 के स्थिर मूल्यों को शुरू करने के लिए एक बार कहा जाता है। ध्यान दें कि C ++ में मेरे कार्यान्वयन में, मैं अहस्ताक्षरित लंबे समय का उपयोग कर रहा हूं, इसलिए जब से आप हस्ताक्षरित होते हैं, आपको >>> ऑपरेटर का उपयोग करना होगा।

सरणी को चेक करने के लिए आंतरिक सीमा की कोई आवश्यकता नहीं है, लेकिन जावा के आशावादी को इस सामान को बहुत जल्दी से पता लगाना है, इसलिए मैं उन्हें इसके लिए दोषी नहीं ठहराता हूं।


3
मुझे यकीन है कि आप दो बार गलत होंगे। 1. Intel sqrt IEEE के अनुरूप है। एकमात्र गैर-अनुरूप निर्देश लैंगे तर्कों के लिए गोनोमेट्रिक निर्देश हैं। 2. जावा Math.sqrt के लिए आंतरिक का उपयोग करता है, कोई JNI नहीं
Maaartinus

1
क्या आप उपयोग करना नहीं भूले pp2? मैं समझता हूं कि pp1छह सबसे महत्वपूर्ण बिट्स के परीक्षण के लिए उपयोग किया जाता है, लेकिन मुझे विश्वास नहीं है कि अगले छह बिट्स का परीक्षण करने का कोई मतलब है।
Maaartinus

6

मुझे कुछ इनपुट पर लगभग सही विधि का उपयोग करने का विचार पसंद है। यहां एक उच्च "ऑफसेट" के साथ एक संस्करण है। कोड काम करने लगता है और मेरे सरल परीक्षण मामले को पास करता है।

बस अपनी जगह:

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

सामान्य बिट लंबाई के लिए विचार करते हुए (हालांकि मैंने यहां विशिष्ट प्रकार का उपयोग किया है), मैंने नीचे के रूप में सरलीकृत अहंकार को डिजाइन करने की कोशिश की। प्रारंभ में 0,1,2 या <0 के लिए सरल और स्पष्ट जांच आवश्यक है। निम्नलिखित इस अर्थ में सरल है कि यह किसी भी मौजूदा गणित कार्यों का उपयोग करने की कोशिश नहीं करता है। अधिकांश ऑपरेटर को बिट-वार ऑपरेटरों से बदला जा सकता है। मैंने किसी भी बेंच मार्क डेटा के साथ परीक्षण नहीं किया है। मैं विशेष रूप से गणित या कंप्यूटर एल्गोरिथ्म डिजाइन में विशेषज्ञ नहीं हूं, मैं आपको समस्या की ओर इशारा करते हुए देखना पसंद करूंगा। मुझे पता है कि वहां बहुत सुधार के मौके हैं।

int main()
{
    unsigned int c1=0 ,c2 = 0;  
    unsigned int x = 0;  
    unsigned int p = 0;  
    int k1 = 0;  
    scanf("%d",&p);  
    if(p % 2 == 0) {  
        x = p/2; 
    }  
    else {  
        x = (p/2) +1;  
    }  
    while(x) 
    {
        if((x*x) > p) {  
            c1 = x;  
            x = x/2; 
        }else {  
            c2 = x;  
            break;  
        }  
    }  
    if((p%2) != 0)  
        c2++;

    while(c2 < c1) 
    {  
        if((c2 * c2 ) == p) {  
            k1 = 1;  
            break;  
        }  
        c2++; 
    }  
    if(k1)  
        printf("\n Perfect square for %d", c2);  
    else  
        printf("\n Not perfect but nearest to :%d :", c2);  
    return 0;  
}  

@ किप: मेरे ब्राउज़र में कुछ समस्या है।
नबाम सिरबंग 15

1
आपको कुछ इंडेंटिंग की जरूरत है।
स्टीव कू ने

5

जब एक वर्ग के अंतिम n बिट्स देखे जाते हैं, तो मैंने सभी संभावित परिणामों की जाँच की। क्रमिक रूप से अधिक बिट्स की जांच करके, 5/6 वीं इनपुट तक को समाप्त किया जा सकता है। मैंने वास्तव में Fermat के फैक्टराइजेशन एल्गोरिदम को लागू करने के लिए इसे डिज़ाइन किया है, और यह वहां बहुत तेज़ है।

public static boolean isSquare(final long val) {
   if ((val & 2) == 2 || (val & 7) == 5) {
     return false;
   }
   if ((val & 11) == 8 || (val & 31) == 20) {
     return false;
   }

   if ((val & 47) == 32 || (val & 127) == 80) {
     return false;
   }

   if ((val & 191) == 128 || (val & 511) == 320) {
     return false;
   }

   // if((val & a == b) || (val & c == d){
   //   return false;
   // }

   if (!modSq[(int) (val % modSq.length)]) {
        return false;
   }

   final long root = (long) Math.sqrt(val);
   return root * root == val;
}

अधिक मूल्यों को खत्म करने के लिए परीक्षणों का विस्तार करने के लिए स्यूडोकोड के अंतिम बिट का उपयोग किया जा सकता है। उपरोक्त परीक्षण k = 0, 1, 2, 3 के लिए हैं

  • a का रूप है (3 << 2k) - 1
  • b फॉर्म का है (2 << 2k)
  • c फॉर्म का है (2 << 2k + 2) - 1
  • d फॉर्म का है (2 << 2k - 1) * 10

    यह पहले परीक्षण करता है कि क्या इसमें दो की शक्ति के माप के साथ एक वर्ग अवशिष्ट है, फिर यह एक अंतिम मापांक के आधार पर परीक्षण करता है, फिर यह अंतिम परीक्षण करने के लिए Math.sqrt का उपयोग करता है। मैं शीर्ष पद से विचार के साथ आया, और उस पर विस्तार करने का प्रयास किया। मैं किसी भी टिप्पणी या सुझाव की सराहना करता हूं।

    अपडेट: एक मापांक, (modSq) और 44352 के मापांक आधार द्वारा परीक्षण का उपयोग करते हुए, मेरा परीक्षण ओपी के अपडेट में एक के समय के 96% में 1,000,000,000 तक की संख्या के लिए चलता है।


  • 2

    यहाँ एक विभाजन और विजय समाधान है।

    यदि प्राकृतिक संख्या का वर्गमूल ( number) एक प्राकृतिक संख्या ( solution) है, तो आप आसानी से एक solutionअंक की संख्या के आधार पर एक सीमा निर्धारित कर सकते हैं number:

    • number1 अंक है: solutionरेंज में = 1 - 4
    • number2 अंक हैं: solutionरेंज में = 3 - 10
    • number3 अंक हैं: 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"

    2
    मुझे अवधारणा पसंद है, लेकिन मैं विनम्रता से एक प्रमुख दोष बताना चाहूंगा: आधार 2 बाइनरी में हैं। बेस 2 को बेस 10 के माध्यम से परिवर्तित toStringकरना बिटकॉइन ऑपरेटरों की तुलना में एक अविश्वसनीय रूप से महंगा ऑपरेशन है। इस प्रकार, प्रश्न के उद्देश्य को पूरा करने के लिए - प्रदर्शन - आपको आधार 10 स्ट्रिंग्स के बजाय बिटवाइज़ ऑपरेटरों का उपयोग करना चाहिए। फिर, मैं वास्तव में आपकी अवधारणा को पसंद करता हूं। इसके बावजूद, आपका कार्यान्वयन (जैसा कि अब यह खड़ा है) सवाल के लिए पोस्ट किए गए सभी संभावित समाधानों में से सबसे धीमा है।
    जैक गिफिन

    1

    यदि गति एक चिंता का विषय है, तो एक लुकअप टेबल पर सबसे अधिक उपयोग किए जाने वाले इनपुट और उनके मूल्यों का विभाजन क्यों नहीं किया जाता है और फिर असाधारण मामलों के लिए जो भी अनुकूलित मैजिक एल्गोरिदम आपके पास आया है, उसे करें?


    समस्या यह है कि "इनपुट का आमतौर पर उपयोग किया जाने वाला सेट" नहीं है - आमतौर पर मैं एक सूची के माध्यम से पुनरावृत्ति कर रहा हूं, इसलिए मैं एक ही इनपुट का दो बार उपयोग नहीं करूंगा।
    किप

    1

    यदि अंतिम X अंक N से बहुत अधिक कुशलता से हो तो 'इसे एक पूर्ण वर्ग नहीं बनाया जा सकता है! मैं java 32 बिट इनट्स का उपयोग करूंगा, और संख्या के अंतिम 16 बिट्स की जांच करने के लिए पर्याप्त डेटा का उत्पादन करूंगा - जो कि 2048 हेक्साडेसिमल मान है।

    ...

    ठीक है। या तो मैंने कुछ संख्या सिद्धांत में भाग लिया है जो मेरे से थोड़ा परे है, या मेरे कोड में एक बग है। किसी भी मामले में, यहाँ कोड है:

    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("};");
    }

    और यहाँ परिणाम हैं:

    (ed: prettify.js में खराब प्रदर्शन के लिए उत्तीर्ण; देखने के लिए संशोधन इतिहास देखें।)


    1

    पूर्णांक अंकगणित के साथ न्यूटन की विधि

    यदि आप गैर-पूर्णांक संचालन से बचना चाहते हैं, तो आप नीचे दी गई विधि का उपयोग कर सकते हैं। यह मूल रूप से पूर्णांक अंकगणित के लिए संशोधित न्यूटन की विधि का उपयोग करता है।

    /**
     * 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। हालांकि, कुछ अन्य पोस्ट में वर्णित फ़िल्टरिंग तंत्र का उपयोग करके इसके प्रदर्शन में सुधार किया जा सकता है।


    1

    न्यूटन की विधि द्वारा वर्गमूलों की गणना करना बहुत ही तेज है ... बशर्ते कि शुरुआती मूल्य उचित हो। हालाँकि, कोई उचित मूल्य नहीं है, और व्यवहार में हम द्वंद्व और लॉग (2 ^ 64) व्यवहार के साथ समाप्त होते हैं।
    वास्तव में तेज़ होने के लिए हमें एक उचित शुरुआती मूल्य पर प्राप्त करने के लिए तेज़ तरीके की आवश्यकता होती है, और इसका मतलब है कि हमें मशीन भाषा में उतरना होगा। यदि कोई प्रोसेसर पेंटियम में POPCNT जैसा कोई निर्देश प्रदान करता है, जो कि अग्रणी शून्य को गिनता है, जिसका उपयोग हम कर सकते हैं कि आधे महत्वपूर्ण बिट्स के साथ एक प्रारंभिक मूल्य हो। देखभाल के साथ हम न्यूटन के चरणों की निश्चित संख्या पा सकते हैं जो हमेशा पर्याप्त होंगे। (इस प्रकार लूप की आवश्यकता को समाप्त करना और बहुत तेज़ निष्पादन करना है।)

    एक दूसरा समाधान फ्लोटिंग पॉइंट सुविधा के माध्यम से हो रहा है, जिसमें एक तेज़ sqrt गणना (i87 सहसंसाधक की तरह हो सकती है।) यहां तक ​​कि एक्सप () और लॉग () के माध्यम से एक भ्रमण बाइनरी खोज में पतित न्यूटन से तेज हो सकता है। इसके लिए एक मुश्किल पहलू है, एक प्रोसेसर निर्भर विश्लेषण क्या और अगर बाद में शोधन आवश्यक है।

    एक तीसरा समाधान थोड़ा अलग समस्या हल करता है, लेकिन यह ध्यान देने योग्य है क्योंकि स्थिति को प्रश्न में वर्णित किया गया है। यदि आप संख्याओं के लिए एक महान कई वर्गमूलों की गणना करना चाहते हैं जो थोड़ा भिन्न होता है, तो आप न्यूटन पुनरावृत्ति का उपयोग कर सकते हैं, यदि आप कभी भी प्रारंभिक मूल्य को पुन: व्यवस्थित नहीं करते हैं, लेकिन बस इसे छोड़ दें जहां पिछली गणना छोड़ दी गई थी। मैंने कम से कम एक यूलर समस्या में सफलता के साथ इसका उपयोग किया है।


    एक अच्छा अनुमान प्राप्त करना बहुत कठिन नहीं है। आप समाधान के लिए निम्न और ऊपरी बाध्यता का अनुमान लगाने के लिए संख्या के अंकों की संख्या का उपयोग कर सकते हैं। मेरा उत्तर भी देखें जहां मैं एक विभाजन का प्रस्ताव करता हूं और समाधान जीतता हूं।
    MWB

    POPCNT और अंकों की संख्या की गिनती में क्या अंतर है? सिवाय इसके कि आप एक नैनोसेकंड में POPCNT कर सकते हैं।
    अल्बर्ट वैन डेर होर्स्ट

    1

    किसी संख्या का वर्गमूल, यह देखते हुए कि संख्या एक पूर्ण वर्ग है।

    जटिलता लॉग (n) है

    /**
     * Calculate square root if the given number is a perfect square.
     * 
     * Approach: Sum of n odd numbers is equals to the square root of n*n, given 
     * that n is a perfect square.
     *
     * @param number
     * @return squareRoot
     */
    
    public static int calculateSquareRoot(int number) {
    
        int sum=1;
        int count =1;
        int squareRoot=1;
        while(sum<number) {
            count+=2;
            sum+=count;
            squareRoot++;
        }
        return squareRoot;
    }

    0

    यदि आप गति चाहते हैं, तो यह देखते हुए कि आपके पूर्णांक परिमित आकार के हैं, मुझे संदेह है कि सबसे तेज़ तरीका शामिल होगा (क) आकार द्वारा मापदंडों को विभाजित करना (जैसे श्रेणियों में सबसे बड़ा बिट सेट), फिर सही वर्गों की एक सरणी के खिलाफ मूल्य की जांच करना उस सीमा के भीतर।


    2
    एक लंबी सीमा में 2 ^ 32 परिपूर्ण वर्ग हैं। यह तालिका बहुत बड़ी होगी। इसके अलावा, एक मेमोरी एक्सेस पर मूल्य की गणना करने का लाभ बहुत बड़ा हो सकता है।
    पीटरऑलेनवेब

    अरे नहीं नहीं, वहाँ 2 ^ 16 हैं। 2 ^ 32 2 ^ 16 वर्ग है। 2 ^ 16 हैं।
    स्वर्गीय एम वेसेल

    3
    हां, लेकिन एक लंबी सीमा 64 बिट्स है, 32 बिट्स नहीं। sqrt (2 ^ 64) = 2 ^ 32। (मैं गणित को थोड़ा आसान बनाने के लिए साइन बिट को अनदेखा कर रहा हूं ... वास्तव में (लंबे) (2 ^ 31.5) = 3037000499 सही वर्ग हैं)
    किप

    0

    कार्मैक विधि के बारे में, ऐसा लगता है कि यह एक बार फिर से पुनरावृत्त करना काफी आसान होगा, जो सटीकता के अंकों की संख्या को दोगुना करना चाहिए। यह सब के बाद, एक बहुत ही छोटा पुनरावृत्त तरीका है - न्यूटन का, बहुत अच्छा पहला अनुमान है।

    आपके वर्तमान सर्वश्रेष्ठ के बारे में, मुझे दो माइक्रो-ऑप्टिमाइज़ेशन दिखाई देते हैं:

    • mod255 का उपयोग करके चेक के बाद 0 बनाम चेक ले जाएँ
    • सामान्य (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;

    जाहिर है, यह जानना दिलचस्प होगा कि प्रत्येक चेकपॉइंट पर कितने नंबर आते हैं - मुझे संदेह है कि चेक वास्तव में स्वतंत्र हैं, जो चीजों को मुश्किल बना देता है।

    हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
    Licensed under cc by-sa 3.0 with attribution required.