නිඛිලයක වර්ග මූලය පූර්ණ සංඛ්‍යාවක් දැයි තීරණය කිරීමට වේගවත්ම ක්‍රමය


1468

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.5පරිපූර්ණ පරීක්ෂණයෙන් පසුව, Math.sqrt () හි ප්‍රති result ලයට එකතු කිරීම අවශ්‍ය නොවන බව මට පෙනී ගියේය, අවම වශයෙන් මගේ යන්ත්‍රයේවත් නැත.
  • මෙම වර්ග මූලය ප්රතිලෝම වේගයෙන් වේගවත්, නමුත් එය n> = 410881. සඳහා වැරදි ප්රතිඵල ලබා දුන් නමුත්, යෝජනා කරන පරිදි BobbyShaftoe , අපි FISR නම්න් n <410881 සඳහා භාවිතා කළ හැකිය.
  • නිව්ටන්ගේ ක්‍රමය වඩා ටිකක් මන්දගාමී විය Math.sqrt(). මෙයට හේතුව Math.sqrt()නිව්ටන්ගේ ක්‍රමයට සමාන දෙයක් භාවිතා කරන නමුත් දෘඩාංග තුළ ක්‍රියාත්මක වන බැවින් එය ජාවා වලට වඩා වේගවත් වීමයි. එසේම, නිව්ටන්ගේ ක්‍රමයට තවමත් යුගල භාවිතය අවශ්‍ය වේ.
  • නවීකරණය කරන ලද නිව්ටන්ගේ ක්‍රමයට උපක්‍රම කිහිපයක් භාවිතා කළ අතර එමඟින් පූර්ණ සංඛ්‍යා ගණිතයට පමණක් සම්බන්ධ වූ අතර පිටාර ගැලීම වළක්වා ගැනීම සඳහා යම් යම් හක්ක අවශ්‍ය විය (මෙම ක්‍රියාව 64-බිට් 64 අත්සන් කළ පූර්ණ සංඛ්‍යා සමඟ ක්‍රියා කිරීමට මට අවශ්‍යය), එය තවමත් වඩා මන්දගාමී විය Math.sqrt().
  • ද්විමය චොප්සි ඊටත් වඩා මන්දගාමී විය. මෙය අර්ථවත් වන්නේ ද්වි-චොප්සි සඳහා සාමාන්‍යයෙන් බිට් 64 සංඛ්‍යාවක වර්ග මූලය සොයා ගැනීමට පාස් 16 ක් අවශ්‍ය වන බැවිනි.
  • ජෝන් ගේ පරීක්ෂණයන්ට අනුව, orප්‍රකාශ භාවිතා කිරීම C ++ හි භාවිතා කිරීමට වඩා වේගවත් වේ switch, නමුත් ජාවා සහ සී # අතර orසහ අතර වෙනසක් නැති බව පෙනේ switch.
  • මම බැලීමේ වගුවක් සෑදීමට ද උත්සාහ කළෙමි (බූලියන් අගයන් 64 ක පුද්ගලික ස්ථිතික අරාවක් ලෙස). එවිට ස්විචය හෝ orප්‍රකාශය වෙනුවට මම කියමි if(lookup[(int)(n&0x3F)]) { test } else return false;. මට පුදුමයට කරුණක් නම් මෙය (මඳක්) මන්දගාමී වීමයි. මෙයට හේතුව ජාවා හි අරාව මායිම් පරීක්ෂා කිරීමයි.

21
මෙය ජාවා කේතය වන අතර එහිදී int == 32 බිටු සහ දිගු == 64 බිටු වන අතර දෙකම අත්සන් කර ඇත.
කිප්

14
H ශ්‍රීවාස්ටා: මම විශාල අගයන් (2 ^ 53 ට වඩා වැඩි) පිළිබඳ යම් පරීක්ෂණයක් කර ඇති අතර ඔබේ ක්‍රමය මඟින් ව්‍යාජ ධනාත්මක කරුණු ලබා දෙයි. පළමු වරට හමු වූයේ n = 9007199326062755 සඳහා වන අතර එය පරිපූර්ණ චතුරස්රයක් නොවන නමුත් එකක් ලෙස ආපසු ලබා දෙනු ලැබේ.
Kip

37
කරුණාකර එය "ජෝන් කාර්මාක් හැක්" ලෙස නොකියන්න. ඔහු ඒකට ආවේ නැහැ.
user9282

84
@ මාමාමා - සමහර විට, නමුත් එය ඔහුට ආරෝපණය කර ඇත. හෙන්රි ෆෝඩ් මෝටර් රථය නිර්මාණය කළේ නැත, රයිට් බ්‍රදර්ස් ගුවන් යානය නිර්මාණය කළේ නැත, සහ පෘථිවිය සූර්යයා වටා භ්‍රමණය වන බව මුලින්ම දැනගත්තේ ගාලෙලියෝ නොවේ ... ලෝකය සොරකම් කරන ලද නව නිපැයුම් වලින් (සහ ආදරය).
රොබට් ෆ්‍රේසර්

4
((1<<(n&15))|65004) != 0වෙනම චෙක්පත් තුනක් තිබීම වැනි දෙයක් භාවිතා කිරීමෙන් ඔබට 'ක්වික්ෆේල්' හි ඉතා සුළු වේගයක් ලබා ගත හැකිය .
නබ්

Answers:


746

අවම වශයෙන් මගේ CPU (x86) සහ ක්‍රමලේඛන භාෂාව (C / C ++) සමඟ ඔබේ 6bit + Carmack + sqrt කේතයට වඩා ~ 35% වේගයෙන් ක්‍රියා කරන ක්‍රමයක් මම හදුනා ගතිමි. ඔබේ ප්‍රති results ල වෙනස් විය හැකිය, විශේෂයෙන් ජාවා සාධකය ක්‍රියා කරන්නේ කෙසේදැයි මම නොදනිමි.

මගේ ප්‍රවේශය තුන් ගුණයකි:

  1. පළමුව, පැහැදිලි පිළිතුරු පෙරහන් කරන්න. Negative ණ සංඛ්‍යා සහ අවසාන බිටු 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. එය එකිනෙකට වෙනස් ප්‍රාථමික තුනක නිෂ්පාදනයක් වන නිසා, මෝඩ් 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.
    අදහස නම්, එක් එක් පුනරාවර්තනයේදී, ඔබ x හි “වත්මන්” වර්ග මූලයට r ට එක් බිට් එකක් එකතු කරයි; සෑම වර්ග මූලයක්ම නිවැරදි මොඩියුලෝ 2 ට වඩා විශාල හා විශාල බලයක්, එනම් ටී / 2 වේ. අවසානයේදී, r සහ t / 2-r x මොඩියුලෝ ටී / 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, t 2 උපක්‍රම z හි විශාලතම බලය බවට පත් කරමි. කෙසේ වෙතත් r හි අගයට බලපාන්නේ නැති t අගයන් මඟ හැරීමට මෙය මට ඉඩ දෙයි. මගේ නඩුවේ පෙර සැකසූ ආරම්භක අගය "කුඩාම ධනාත්මක" වර්ග මූල මොඩියුලෝ 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
වාව්! මම මෙය ජාවා බවට පරිවර්තනය කර සංසන්දනයක් කිරීමට උත්සාහ කරමි, ප්‍රති .ල පිළිබඳ නිරවද්‍යතාව පරීක්ෂා කරන්නෙමි. මම සොයා ගන්නා දේ මම ඔබට දන්වන්නම්.
කිප්

82
වාව්, මේක ලස්සනයි. මම මීට පෙර හෙන්සල් එසවීම දුටුවෙමි (බහුපදවල මූලයන් ගණනය කිරීම මොඩියුලෝ ප්‍රයිම්) නමුත් සංඛ්‍යා වර්ග වර්ග ගණනය කිරීම සඳහා ලෙමාව පරිස්සමින් පහත් කළ හැකි බව මම දැන සිටියේ නැත; මෙය ... නඟා සිටුවීම :)
ශ්‍රීවාට්සාර්

3
@nightcracker එය එසේ නොවේ. 9 < 0 => false, 9&2 => 0, 9&7 == 5 => false, 9&11 == 8 => false.
ප්‍රීමෝ

55
මාටිනස් 2x වේගවත් විසඳුමක් (හා ඊට වඩා කෙටි) පහළට පහළින් පළ කළේය, මඳක් පසුව, එය එතරම් ආදරය ලබා ගන්නා බවක් නොපෙනේ.
ජේසන් සී

3
පැහැදිලි චතුරස්රයන් පෙරීම මගින් විවිධ විසඳුම්වල වේග වාසියක් ලබා ගන්නා බව පෙනේ. මාටිනස්ගේ විසඳුම හරහා පෙරීම සහ වර්ගීකරණ ශ්‍රිතය එය ගොඩනංවන ලද ශ්‍රිතයක් ලෙස භාවිතා කිරීමේ තත්වය යමෙකු මිණුම් සලකුණු කර තිබේද?
user1914292

388

මම සාදයට සෑහෙන්න පරක්කුයි, නමුත් වඩා හොඳ පිළිතුරක් දීමට මම බලාපොරොත්තු වෙමි; කෙටි සහ (මගේ මිණුම් ලකුණ නිවැරදි යැයි උපකල්පනය කිරීම) ද වඩා වේගවත් ය .

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.numberOfTrailingZerosJIT-ed තනි i86 උපදෙස් වලට ඇතුළත් වන බැවින් මෙම ක්‍රමය ඉතා වේගවත්ය.

පසුපස ඇති ශුන්‍යයන් අතහැර දැමීමෙන් පසුව, තෙවන පරීක්ෂණය 011, 101, හෝ 111 සමඟ ද්විමය වශයෙන් අවසන් වන සංඛ්‍යා හසුරුවයි, ඒවා පරිපූර්ණ චතුරස්රයන් නොවේ. එය negative ණ සංඛ්‍යා ගැන සැලකිලිමත් වන අතර 0 ද හසුරුවයි.

අවසාන පරීක්ෂණය නැවත doubleගණිතයට වැටේ . ලෙස doubleබිටු 53 ක් පමණක් mantissa ඇත, සිට පරිවර්තනය longකිරීමට doubleවිශාල වටිනාකම් සඳහා යයි වරදවා වටහා ඇතුළත් වේ. එසේ වුවද, පරීක්ෂණය නිවැරදි ය ( සාක්ෂි වැරදියි නම් හැර ).

Mod255 අදහස ඇතුළත් කිරීමට උත්සාහ කිරීම සාර්ථක නොවීය.


3
මාර්‍ගයේ වටිනාකම ව්‍යාජ ලෙස වසං කිරීම ටිකක් ... නපුරකි. එය ජාවා පිරිවිතරයේ ඇත්තේ මන්දැයි ඔබට යම් අදහසක් තිබේද?
dfeuer

6
fedfeuer හේතු දෙකක් ඇති බව මම අනුමාන කරමි: 1. වැඩි වැඩියෙන් මාරුවීම තේරුමක් නැත. 2. එය එච්ඩබ්ලිව් වැඩ වැනි වන අතර බිට්වේස් මෙහෙයුම් භාවිතා කරන ඕනෑම අයෙකු කාර්ය සාධනය කෙරෙහි උනන්දුවක් දක්වයි, එබැවින් වෙනත් දෙයක් කිරීම වැරදිය. - මෙම goodMaskපරීක්ෂණය එය, නමුත්, එය කරන්නේ පෙර අයිතිය මාරුව. එබැවින් ඔබට එය නැවත කිරීමට සිදුවේ, නමුත් මේ ආකාරයෙන් එය වඩාත් සරල වන අතර AFAIK ඉතා වේගවත් හා සමානව හොඳයි.
maaartinus

3
feffeuer මිණුම් ලකුණ සඳහා ASAP ට පිළිතුරු සැපයීම වැදගත් වන අතර, පසුපස ඇති ශුන්‍ය ගණනට පිළිතුරක් නොලැබේ; එය සූදානම් කිරීමේ පියවරක් පමණි. i86 / amd64 එය කරන්න. මොබයිල් වල ඇති කුඩා CPU ගැන කිසිම අදහසක් නැත, නමුත් නරකම දෙය නම්, ජාවා විසින් ඔවුන් සඳහා AND සහ උපදෙස් ජනනය කළ යුතු අතර එය අනෙක් පැත්තට වඩා සරල ය.
maaartinus

2
බොහෝ විට @Sebastian වඩා හොඳ ටෙස්ට්: if ((x & (7 | Integer.MIN_VALUE)) != 1) return x == 0;.
maaartinus

4
"ද්විත්වයට ඇත්තේ බිටු 56 ක් පමණි." -> මම කියන්නේ එයට බිට් 53 ක් ඇති බවයි. එසේම
චක්ස් - මොනිකා

133

ඔබට මිණුම් සලකුණු කිහිපයක් කිරීමට සිදුවේ. හොඳම ඇල්ගොරිතම ඔබගේ යෙදවුම් බෙදා හැරීම මත රඳා පවතී.

ඔබේ ඇල්ගොරිතම බොහෝ දුරට ප්‍රශස්ත විය හැකි නමුත්, ඔබේ වර්ග මූල චර්යාව ඇමතීමට පෙර සමහර හැකියාවන් බැහැර කිරීමට ඉක්මන් පරීක්‍ෂණයක් කිරීමට ඔබට අවශ්‍ය විය හැකිය. නිදසුනක් ලෙස, ටිකක් බුද්ධිමත් "සහ." කිරීමෙන් හෙක්ස් හි ඔබේ අංකයේ අවසාන ඉලක්කම් දෙස බලන්න. පරිපූර්ණ චතුරස්රයන් අවසන් විය හැක්කේ 16 වන පාදයේ 0, 1, 4, හෝ 9 න් පමණි, එබැවින් ඔබේ යෙදවුම් වලින් 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 ++ හි ප්‍රතිසම කේතය පරීක්ෂා කළ විට එය ඇත්ත වශයෙන්ම මුල් පිටපතට වඩා මන්දගාමී විය. කෙසේ වෙතත්, මම ස්විච් ප්‍රකාශය ඉවත් කළ විට, හෙක්ස් ට්‍රික් එක නැවත වරක් කේතය දෙගුණයක් වේගවත් කරයි.

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
ArLarsH 0.5 එකතු කිරීමට අවශ්‍ය නැත, සාක්ෂි සඳහා සබැඳියක් සඳහා මගේ විසඳුම බලන්න.
maaartinus

2
Er ජෙරීගෝයාල් එය සම්පාදකයා සහ නඩු වල අගයන් මත රඳා පවතී. පරිපූර්ණ සම්පාදකයෙකු තුළ, ස්විචයක් සෑම විටම අවම වශයෙන් වේගවත් වේ. නමුත් සම්පාදකයින් පරිපූර්ණ නොවේ, එබැවින් ජෝන් කළාක් මෙන් එය අත්හදා බැලීම වඩාත් සුදුසුය.
fishinear

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

එය ක්‍රියාත්මක වන ආකාරය පිළිබඳ වැඩි විස්තර සඳහා ඔබට සබැඳිය අනුගමනය කළ හැකිය, නමුත් ඔබ එතරම් සැලකිල්ලක් නොදක්වන්නේ නම්, මෙය දළ වශයෙන් බ්ලොග් අඩවිය කියවීමෙන් සහ සංඛ්‍යාත්මක විශ්ලේෂණ පා course මාලාව හැදෑරීමෙන් මට මතකයි:

  • මෙම * (long*) &yපූර්ණ සංඛ්යාමය මෙහෙයුම් අමු බයිට් මත යෙදිය හැකි නිසා මූලික වශයෙන් වේගවත් ආගමට හරවා-දිගු කාර්යයකි.
  • මෙම 0x5f3759df - (i >> 1);රේඛාව ආසන්න උත්සවය සඳහා පෙර-ගනන් බලා බීජ අගය වේ.
  • මෙම * (float*) &iඉපිලුම් ලක්ෂ්ය සඳහා නැවත අගය බවට පරිවර්තනය කරයි.
  • මෙම y = y * ( threehalfs - ( x2 * y * y ) )රේඛාව bascially නැවත උත්සවය පුරා වටිනාකම දයානන්ද්.

ආසන්න ශ්‍රිතය මඟින් වඩාත් නිවැරදි අගයන් ලබා දෙන අතර ප්‍රති the ලය මත ඔබ ශ්‍රිතය නැවත ප්‍රකාශ කරයි. ක්වේක්ගේ සිද්ධියේදී, එක් පුනරාවර්තනයක් "ප්‍රමාණවත්", නමුත් එය ඔබ සඳහා නොවේ නම් ... එවිට ඔබට අවශ්‍ය තරම් පුනරාවර්තනය එකතු කළ හැකිය.

මෙය වේගවත් විය යුත්තේ එය බොළඳ චතුරස්රයේ මුල් බැසීමේ * 0.5Fමෙහෙයුම් ගණන 2 කින් සරල බෙදීමකට අඩු කරන බැවිනි (ඇත්ත වශයෙන්ම ගුණ කිරීමේ මෙහෙයුමක්) සහ ඒ වෙනුවට ස්ථාවර ගුණ කිරීමේ මෙහෙයුම් කිහිපයක් සමඟ එය ආදේශ කරන්න.


9
මෙය වර්ග 1 / වර්ග (අංකය) මිස වර්ග (අංකය) නොවන බව සැලකිල්ලට ගත යුතුය. මම යම් පරීක්ෂණයක් කර ඇති අතර මෙය n = 410881 සිට ආරම්භ වේ: ජෝන් කාර්මාක් මැජික් සූත්‍රය 642.00104, සත්‍ය වර්ග මූල 641 වන විට නැවත පැමිණේ.
කිප්

11
වේගවත් ප්‍රතිලෝම වර්ග මූලයන් මත ඔබට ක්‍රිස් ලොමොන්ට්ස් කඩදාසි දෙස බැලිය හැකිය: lomont.org/Math/Papers/2003/InvSqrt.pdf එය මෙහි ඇති තාක්‍ෂණයම භාවිතා කරයි, නමුත් වෙනස් මැජික් අංකයකින්. මැජික් අංකය තෝරාගත්තේ මන්දැයි පුවත්පත පැහැදිලි කරයි.

4
එසේම, 3d.com/content/articles/8 සහ 3d.com/content/articles/15 වලින් ඔබ්බට මෙම ක්‍රමයේ මූලාරම්භය පිළිබඳව යම් ආලෝකයක් හෙළි කරයි. එය බොහෝ විට ජෝන් කාර්මාක් වෙත ආරෝපණය කර ඇති නමුත් මුල් කේතය ගැරී ටාරෝලි, ග්‍රෙග් වොල්ෂ් සහ තවත් අය විසින් ලියන ලද්දක් බව පෙනේ.

3
ඔබට ජාවා හි පාවෙන සහ තීන්ත ටයිප් කළ නොහැක.
ඇන්ටිමනි

10
Nt ඇන්ටිමනි කියන්නේ කවුද? FloatToIntBits සහ IntToFloatBits ජාවා 1.0.2 සිට පැවතුනි.
corsiKa

38

එය වේගවත් ද, නැතහොත් නිවැරදි ද යන්න මට විශ්වාස නැත, නමුත් ඔබට වර්ග මූලය වේගයෙන් විසඳීම සඳහා ජෝන් කාර්මාක්ගේ මැජික් චතුරස්රය , ඇල්ගොරිතම භාවිතා කළ හැකිය . බිට් නිඛිල 32 සඳහා ඔබට මෙය පහසුවෙන් පරීක්‍ෂා කළ හැකි අතර, ඔබට සත්‍ය ප්‍රති results ල ලැබී ඇති බව තහවුරු කර ගත හැකිය. කෙසේ වෙතත්, දැන් මම ඒ ගැන සිතන විට, යුගල භාවිතා කිරීම ද ආසන්න වශයෙන් වේ, එබැවින් එය ක්‍රියාත්මක වන්නේ කෙසේදැයි මට විශ්වාස නැත.


10
මම විශ්වාස කරනවා මේ දිනවල කාර්මාක්ගේ උපක්‍රමය තරමක් අර්ථ විරහිතයි. සාදන ලද වර්ග අඩි උපදෙස් පෙරට වඩා බොහෝ වේගවත් ය, එබැවින් සාමාන්‍ය වර්ග මූලයක් සිදු කිරීම සහ ප්‍රති result ලය පූර්ණ සංඛ්‍යාවක් දැයි පරීක්ෂා කිරීම වඩා හොඳ විය හැකිය. සෑම විටම මෙන්, එය මිණුම් සලකුණු කරන්න.
jalf

4
මෙය n = 410881 සිට ඇරඹෙන අතර, ජෝන් කාර්මාක් මැජික් සූත්‍රය 642.00104, සත්‍ය වර්ග මූල 641 වන විට නැවත පැමිණේ.
Kip

11
මම මෑතකදී ජාවා ක්‍රීඩාවකදී කාර්මාක්ගේ උපක්‍රමය භාවිතා කළ අතර එය ඉතා effective ලදායී වූ අතර එය 40% ක වේගයක් ලබා දුන්නේය, එබැවින් එය තවමත් ප්‍රයෝජනවත් වේ, අවම වශයෙන් ජාවාහි.
finnw

3
Frame රොබට් ෆ්‍රේසර් ඔව් + සමස්ත රාමු අනුපාතයෙන් 40%. ක්රීඩාව වර්ග මූලය ශ්රිතය හා (. මම ද නම්න් හැරියෙමි සමාන ටිකක් භාවිතා ප්රචලිත කෙරෙන කර තිබූ) වටය-කිරීමට ආසන්නතම-පූර්ණ සංඛ්යාමය කාර්යය විසින් ආධිපත්යය ලබා ගත හැකි CPU පැදි කට ආසන්න සියලු, රැගෙන වන අංශු භෞතික ක්රමයේ
finnw

5
සබැඳිය කැඩී ඇත.
පික්සාර්

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 ක් පමණි.


මගේ මුදල් මේ ආකාරයේ ප්‍රවේශයක් මත ය. සම්පූර්ණ වර්ග මූලයක් ගණනය කරන බැවින් වර්ග () ඇමතීමෙන් වළකින්න, ඔබට අවශ්‍ය වන්නේ පළමු ඉලක්කම් කිහිපයක් පමණි.
පීටර්ඇලන් වෙබ්

3
අනෙක් අතට, පාවෙන ස්ථානය විශේෂිත එෆ්පී ඒකකයක සිදු කරන්නේ නම්, එය සියලු ආකාරයේ විනෝද උපක්‍රම භාවිතා කරයි. මිණුම් ලකුණක් නොමැතිව ඔට්ටු ඇල්ලීමට මා කැමති නැත :) (අද රාත්‍රියේ සී # හි වුවද එය බැලීමට උත්සාහ කරමි ...)
ජෝන් ස්කීට්

8
දෘඩාංග වර්ග මේ දිනවල ඇත්තෙන්ම වේගවත් ය.
ඇඩම් රොසෙන්ෆීල්ඩ්

24

මම මෙම ත්‍රෙඩ් එකේ ඇල්ගොරිතම කිහිපයක් පිළිබඳව මගේම විශ්ලේෂණයක් පවත්වා නව ප්‍රති .ල කිහිපයක් ඉදිරිපත් කළෙමි. මෙම පිළිතුරේ සංස්කරණ ඉතිහාසයේ ඔබට එම පැරණි ප්‍රති results ල දැකිය හැකිය, නමුත් ඒවා වැරදියි, මම වැරැද්දක් කළ නිසාත්, සමීප නොවන ඇල්ගොරිතම කිහිපයක් විශ්ලේෂණය කරමින් කාලය නාස්ති කළ නිසාත් ඒවා නිවැරදි නොවේ. කෙසේ වෙතත්, විවිධ පිළිතුරු කිහිපයකින් පාඩම් උකහා ගනිමින්, මට දැන් මෙම ත්‍රෙඩ් එකේ “ජයග්‍රාහකයා” තලා දමන ඇල්ගොරිතම දෙකක් තිබේ. මෙන්න මම අනෙක් සියල්ලන්ට වඩා වෙනස් ලෙස කරන මූලික දෙය:

// 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ප්‍රකාශය එක නම් ප්‍රකාශය බෙහෙවින් සරල කරයි . කෙසේ වෙතත්, පරීක්ෂා කරන ලද බොහෝ සංඛ්‍යා වලට සැලකිය යුතු බලයේ සාධක දෙකක් තිබේ නම් එය ධාවන කාලයට එකතු කළ හැකිය.

පහත ඇල්ගොරිතම පහත පරිදි වේ:

  • අන්තර්ජාල - Kip ගේ අභ්යාසයක් පිළිතුර
  • ඩුරොන් - එක්-පාස් පිළිතුර පදනම් ලෙස භාවිතා කරමින් මගේ වෙනස් කළ පිළිතුර
  • DurronTwo - මගේ නවීකරණය කරන ලද පිළිතුර ද්වි-පාස් පිළිතුර භාවිතා කරමින් (@ ජොන්නි හෙග්හයිම් විසින්), තවත් සුළු වෙනස් කිරීම් සමඟ.

මෙන්න සංඛ්‍යා ජනනය කරන්නේ නම් නියැදි ධාවන වේලාවකි 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;
}

මගේ මිණුම් ලකුණ: (ගූගල් කැලිපර 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;

එහි ප්‍රති ing ලයක් ලෙස දර්ශකය 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 දෘඩාංග වර්ග අඩි (~ 20 චක්‍ර) වලට වඩා මන්දගාමී වේ (~ 100 සිට 150 දක්වා). ප්‍රති put ල අනුව, ඔබට කැපී පෙනෙන හැඹිලි බොහොමයක් පවත්වා ගත හැකිය, නමුත් ඔබ තවමත් වෙනත් ප්‍රයෝජනවත් දත්ත ඉවත් කරමින් සිටී. විශාල බැලීමේ වගුවක් එය වටිනවා එය වෙනත් විකල්පයකට වඩා වේගවත් නම් පමණක් වන අතර මෙම ක්‍රියාව ඔබේ සමස්ත වැඩසටහනේ ක්‍රියාකාරිත්වයේ ප්‍රධාන සාධකය විය.
පීටර් කෝර්ඩ්ස්

1
W ස්විස් ෆ්‍රෑන්ක්: ඔබේ වැඩසටහන කරන එකම දෙය පරිපූර්ණ චතුරස්රයක්ද ? බැලුම් වගුව මයික්‍රොබෙන්ච් සලකුණකින් එය නැවත නැවත තද පුඩුවක් ලෙස හැඳින්විය හැකි නමුත් එහි වැඩකරන කට්ටලයේ වෙනත් දත්ත ඇති සැබෑ වැඩසටහනක එය හොඳ නැත.
පීටර් කෝර්ඩ්ස්

1
0x1FFFFFE බිටු සිතියමක් මෙගා බයිට් 4 ක් ගනීඇසුරුම් කළ බිට්මැප් ලෙස ගබඩා කර ඇත්නම් . ක L3 හැඹිලි පහර නවීන ඉන්ටෙල් ඩෙස්ක්ටොප් එක මත විශාල Xeon මත> 40 පමාව පිළිබඳ සයිකල් සහ වඩාත් නරක අතට ඇත; දෘඩාංග sqrt + mul ප්‍රමාදයට වඩා දිගු. අගයකට බයිට් 1 බැගින් බයිට්- සිතියමක් ලෙස ගබඩා කර ඇත්නම් , එය 32 MB පමණ වේ; ඕනෑම දෙයක L3 හැඹිලියට වඩා විශාල වන නමුත් සියලු හරයන් එක් විශාල හැඹිලියක් බෙදා ගන්නා බොහෝ හරය සහිත Xeon. එබැවින් ඔබේ ආදාන දත්ත විශාල ප්‍රමාණයේ යෙදවුම් වලට වඩා ඒකාකාර අහඹු ලෙස බෙදා හැරීමක් තිබේ නම්, තද පුඩුවක් තුළ වුවද ඔබට L2 හැඹිලි ගොඩක් ලැබෙනු ඇත. (ඉන්ටෙල් හි පුද්ගලික පර්යන්ත L2 256k පමණක් වන අතර cycle 12 චක්‍රීය ප්‍රමාදයක් ඇත.)
පීටර් කෝර්ඩ්ස්

1
W ස්විස්ෆ්‍රෑන්ක්: ඔහ්, ඔබ කරන්නේ මූල පරීක්ෂාව නම්, L3 පහර ලබා ගැනීම සඳහා බිට්මැප් සමඟ මෙය කළ හැකිය. මම ප්‍රමාද වීම දෙස බැලුවෙමි, නමුත් බොහෝ මග හැරීම් එකවර පියාසර කළ හැකිය, එබැවින් ප්‍රතිදානය හොඳ විය හැකිය. OTOH, SIMD ප්‍රතිදානය sqrtpsහෝ sqrtpd(ද්විත්ව නිරවද්‍යතාව) ස්කයිලේක්හි එතරම් නරක නැත, නමුත් පැරණි CPU වල ප්‍රමාදයට වඩා හොඳ නොවේ. කෙසේ වෙතත් 7-cpu.com/cpu/Haswell.html සතුව හොඳ පර්යේෂණාත්මක අංක කිහිපයක් සහ වෙනත් CPU සඳහා පිටු තිබේ. ඇග්නර් ෆෝග්ගේ මයික්‍රොආර්ච් මාර්ගෝපදේශය පීඩීඑෆ් හි ඉන්ටෙල් සහ ඒඑම්ඩී uarches සඳහා හැඹිලි ප්‍රමාද අංක කිහිපයක් ඇත: agner.org/optimize
පීටර්

1
ජාවා වෙතින් x86 SIMD භාවිතා කිරීම ගැටළුවක් වන අතර, ඔබ int-> fp සහ fp-> int පරිවර්තන පිරිවැය එකතු කරන විට, බිට්මැප් වඩා හොඳ විය හැකි බව පිළිගත හැකිය. double+ -2 ^ 24 පරාසයෙන් පිටත යම් සංඛ්‍යාවක් නිම කිරීම වළක්වා ගැනීමට ඔබට නිරවද්‍යතාව අවශ්‍ය වේ (එබැවින් බිටු 32 ක පූර්ණ සංඛ්‍යාවක් ඊට පිටතින් විය හැක), sqrtpdඑය මන්දගාමී වන අතර උපදෙස් වලට වඩා sqrtpsමූලද්‍රව්‍යවලින් අඩක් පමණක් සැකසීම (සිම්ඩ් දෛශිකයකට) .
පීටර් කෝඩ්ස්

18

පූර්ණ සංඛ්‍යා චතුරස්රය ගණනය කිරීම සඳහා නිව්ටන්ගේ ක්රමය භාවිතා කිරීම වඩා වේගවත් විය යුතුය , ඉන්පසු මෙම අංකය වර්ග කර ඔබේ වර්තමාන විසඳුමේ දී මෙන් පරීක්ෂා කරන්න. වෙනත් පිළිතුරු වල සඳහන් කර ඇති කාර්මාක් විසඳුම සඳහා පදනම නිව්ටන්ගේ ක්‍රමයයි. ඔබ මූලයේ නිඛිල කොටස ගැන පමණක් උනන්දු වන බැවින් ඔබට වේගවත් පිළිතුරක් ලබා ගත හැකි අතර, ආසන්න වශයෙන් ඇල්ගොරිතම ඉක්මනින් නැවැත්වීමට ඔබට ඉඩ සලසයි.

ඔබට උත්සාහ කළ හැකි තවත් ප්‍රශස්තිකරණයක්: අංකයක ඩිජිටල් මූලය 1, 4, 7, හෝ 9 වලින් අවසන් නොවන්නේ නම්, එම අංකය පරිපූර්ණ වර්ගයක් නොවේ. මන්දගාමී වර්ග මූල ඇල්ගොරිතම යෙදීමට පෙර ඔබේ යෙදවුම් වලින් 60% ක් ඉවත් කිරීමට මෙය ඉක්මන් ක්‍රමයක් ලෙස භාවිතා කළ හැකිය.


1
ඩිජිටල් මූලයන් මොඩියුලෝවට තදින්ම සමාන වේ, එබැවින් මොඩ් 16 සහ මොඩ් 255 වැනි වෙනත් මොඩියුලෝ ක්‍රම සමඟ සලකා බැලිය යුතුය.
ක්‍රිස්ටියන් ඕඩාර්ඩ්

1
ඩිජිටල් මූල මොඩියුලයට සමාන බව ඔබට විශ්වාසද? සබැඳිය පැහැදිලි කළ පරිදි එය සම්පූර්ණයෙන්ම වෙනස් දෙයක් ලෙස පෙනේ. ලැයිස්තුව 1,4,7,9 නොව 1,4,5,9 බව සැලකිල්ලට ගන්න.
Fractaly

1
දශම පද්ධතියේ ඩිජිටල් මූල මොඩියුලෝ 9 (හොඳින් dr (n) = 1 + ((n-1) mod 9) භාවිතා කිරීමට සමාන වේ; එබැවින් සුළු වෙනසක් ද සිදු වේ). අංක 0,1,4,5,9 මොඩියුලෝ 16 සඳහා වන අතර 0, 1, 4, 7 මොඩියුලෝ 9 සඳහා වේ - ඒවා ඩිජිටල් මූල සඳහා 1, 4, 7, 9 ට අනුරූප වේ.
හාන්ස් ඔල්සන්

15

මෙම ක්‍රියාව 64-බිට් 64 අත්සන් කළ පූර්ණ සංඛ්‍යා සමඟ ක්‍රියා කිරීමට මට අවශ්‍යය

Math.sqrt()ආදාන පරාමිතීන් ලෙස දෙගුණයක් සමඟ ක්‍රියා කරයි, එබැවින් ඔබට 2 ^ 53 ට වඩා විශාල සංඛ්‍යාවක් සඳහා නිවැරදි ප්‍රති results ල නොලැබේ .


5
2 ^ 53 ට වඩා විශාල සියලු පරිපූර්ණ චතුරස්රවල මෙන්ම සෑම පරිපූර්ණ චතුරස්රයකටම පහළින් 5 සිට සෑම පරිපූර්ණ චතුරස්රයකටම ඉහළින් ඇති සියලු සංඛ්යා වල ඇත්ත වශයෙන්ම මම පිළිතුර පරීක්ෂා කර ඇති අතර මට නිවැරදි ප්රති .ලය ලැබේ. (වටකුරු දෝෂය නිවැරදි කරනුයේ මම වර්ග පිළිතුර දිගු, පසුව එම අගය වර්ග කර සංසන්දනය කරමිනි)
Kip

2
Ip කිප්: මම හිතන්නේ එය ක්‍රියාත්මක වන බව මම ඔප්පු කර ඇත්තෙමි .
maaartinus

ප්‍රති results ල හරියටම නිවැරදි නැත, නමුත් ඔබ සිතනවාට වඩා නිවැරදි ය. ද්විත්ව හා වර්ග මූලයට පරිවර්තනය කිරීමෙන් පසු අවම වශයෙන් නිවැරදි ඉලක්කම් 15 ක් වත් උපකල්පනය කළහොත්, එය ඕනෑ තරම් තිබේ, මන්ද අපට බිටු 32 වර්ග මූල සඳහා ඉලක්කම් 11: 10 ට වඩා අවශ්‍ය නොවන අතර දශම ස්ථානයක් සඳහා 1 ට වඩා අඩුය. +0.5 වටය ආසන්නතම සිට.
mwfearnley

3
Math.sqrt () මුළුමනින්ම නිවැරදි නොවේ, නමුත් එය එසේ කළ යුතු නැත. පළමු පෝස්ට් එකේම, tst යනු වර්ග (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 ට පැමිණියේ නම්, ආදාන අංකය ප්‍රාථමික සංඛ්‍යා වර්ගයක වියෝජනයකි. චතුරස්රය සංඛ්‍යාවට වඩා විශාල වුවහොත්, මෙම චතුරස්රයට හෝ විශාල චතුරස්රයන්ට එය බෙදිය හැකි ක්රමයක් නොමැත, එබැවින් සංඛ්යාව ප්රාථමික සංඛ්යා වල වර්ග වියෝජනය විය නොහැකිය.

වර්තමානයේ දෘඩාංග වලින් කරන ලද වර්ග ප්‍රමාණය සහ මෙහි ප්‍රාථමික සංඛ්‍යා ගණනය කිරීමේ අවශ්‍යතාවය සැලකිල්ලට ගෙන, මෙම විසඳුම මන්දගාමී බව මම අනුමාන කරමි. නමුත් එය 2 ^ 54 ට වඩා වැඩ නොකරන වර්ග අඩි විසඳුමට වඩා හොඳ ප්‍රති results ල ලබා දිය යුතුය.


1
වත්මන් දෘඩාංග මත FP වර්ගයට වඩා පූර්ණ සංඛ්‍යා බෙදීම මන්දගාමී වේ. මෙම අදහසට අවස්ථාවක් නැත. >. <2008 දී පවා Core2 හි sqrtsdප්‍රතිදානය 6-58c ට එකකි. එය idivපාපැදි 12-36 කට එකකි. (ප්‍රතිදාන වලට සමාන ප්‍රමාදයන්: කිසිදු ඒකකයක් නල මාර්ගයක් නොමැත).
පීටර් කෝර්ඩ්ස්

sqrt පරිපූර්ණ ලෙස නිවැරදි වීමට අවශ්‍ය නැත. ප්‍රති result ලය පූර්ණ සංඛ්‍යා වර්ගීකරණය කිරීමෙන් සහ ආදාන නිඛිලයට නිශ්චිත පූර්ණ සංඛ්‍යා වර්ගයක් තිබේදැයි තීරණය කිරීම සඳහා පූර්ණ සංඛ්‍යාවක් සංසන්දනය කිරීමෙන් ඔබ පරීක්ෂා කරන්නේ එබැවිනි.
පීටර් කෝර්ඩ්ස්

11

dපරිපූර්ණ චතුරස්රයක අවසාන ඉලක්කම් ගත හැක්කේ නිශ්චිත අගයන් මත පමණක් බව පෙන්වා දී ඇත . dසංඛ්‍යාවක අවසාන ඉලක්කම් (පාදමෙහි b) බෙදූ nවිට ඉතිරියට සමාන nවේ bd, එනම්. සී අංකනයෙන් n % pow(b, d).

මෙය ඕනෑම මොඩියුලයකට සාමාන්‍යකරණය කළ හැකිය m , එනම්. n % mසංඛ්‍යා වලින් යම් ප්‍රතිශතයක් පරිපූර්ණ චතුරස්රයන්ගෙන් බැහැර කිරීම සඳහා භාවිතා කළ හැකිය. ඔබ දැනට භාවිතා කරන මාපාංකය 64 වන අතර එය 12 ට ඉඩ දෙයි. ඉතිරි කොටස් වලින් 19%, හැකි තරම් වර්ග. කුඩා කේතීකරණයක් සමඟ 110880 මොඩියුලය මට හමු විය, එය 2016 ට පමණක් ඉඩ දෙයි, එනම්. ඉතිරි කොටස් වලින් 1.8% ක් හැකි තරම් වර්ග. එබැවින් මොඩියුලස් මෙහෙයුමක පිරිවැය (එනම් බෙදීම) සහ ඔබේ යන්ත්‍රයේ වර්ග මූලයට එදිරිව වගු බැලීම මත පදනම්ව, මෙම මාපාංකය භාවිතා කිරීම වේගවත් විය හැකිය.

මාර්ගය වන විට, බැලීමේ වගුව සඳහා ඇසුරුම් කළ බිටු සමූහයක් ගබඩා කිරීමට ජාවාට ක්‍රමයක් තිබේ නම්, එය භාවිතා නොකරන්න. 110880 32-බිට් වචන මේ දිනවල එතරම් RAM නොවන අතර යන්ත්‍ර වචනයක් ලබා ගැනීම එක් බිට් එකක් ලබා ගැනීමට වඩා වේගවත් වනු ඇත.


හොඳයි. ඔබ මෙය වීජීයව හෝ අත්හදා බැලීමෙන් හා දෝෂයකින් සිදු කළාද? එය එතරම් effective ලදායී වන්නේ මන්දැයි මට පෙනේ - උදා: පරිපූර්ණ චතුරස්රයන් අතර ගැටුම් ගොඩක්, උදා. 333 ^ 2% 110880 == 3 ^ 2, 334 ^ 2% 110880 == 26 ^ 2, 338 ^ 2% 110880 == 58 ^ 2 .. .
finnw

IIRC එය තිරිසන් බලයක් වූ නමුත් 110880 = 2 ^ 5 * 3 ^ 2 * 5 * 7 * 11, එමඟින් 6 * 3 * 2 * 2 * 2 - 1 = 143 නිසි බෙදුම්කරුවන් ලබා දෙන බව සලකන්න.
හියු ඇලන්

බැලීමේ සීමාවන් නිසා 44352 වඩා හොඳින් ක්‍රියා කරන අතර 2.6% සමත් අනුපාතයක් ඇති බව මට පෙනී ගියේය. අවම වශයෙන් මගේ ක්‍රියාත්මක කිරීමේදී.
Fractaly

1
වත්මන් x86 දෘඩාංග මත idivFP sqrt ( sqrtsd) ට සමාන්තර බෙදීම ( ) සමාන හෝ නරක ය . එසේම, බිට්ෆීල්ඩ් වළක්වා ගැනීමෙන් සම්පූර්ණයෙන්ම එකඟ නොවේ. හැඹිලි පහර අනුපාතය බිට්ෆීල්ඩ් සමඟ ටොන් වඩා හොඳ වනු ඇති අතර, බිට්ෆීල්ඩ් එකක ටිකක් පරීක්ෂා කිරීම සම්පූර්ණ බයිටයක් පරීක්ෂා කිරීමට වඩා සරල උපදෙස් එකක් හෝ දෙකක් පමණි. (Ints 32bit නොවන සුදුසු පූර්වාපේක්ෂීව සංචිත කෙරී පවා-bitfields නොවන ලෙස, බයිට මාලාවක් හොඳම බව ඉතා කුඩා වගු සඳහා, x86 32bit dword සමාන වේගය තනි-බයිට ප්රවේශය ඇත..)
පීටර් Cordes

11

නිඛිල ගැටළුවක් පූර්ණ සංඛ්‍යාවක් සඳහා සුදුසු වේ. මේ අනුව

(නිෂේධාත්මක නොවන) නිඛිලවල ද්විමය සෙවීමක් කරන්න t**2 <= n. ඉන්පසු පරීක්ෂා කරන්නr**2 = n හරියටම . මේ සඳහා කාලය ගතවේ O (log n).

කට්ටලය අසීමිත බැවින් ධනාත්මක නිඛිල ද්විමය සෙවීම ඔබ නොදන්නේ නම්, එය පහසුය. ඔබ ආරම්භ කරන්නේ ඔබේ වැඩිවන ශ්‍රිතය f (ඉහළ f(t) = t**2 - n) දෙකක බලයන් මත ගණනය කිරීමෙනි. එය ධනාත්මක වන බව ඔබ දුටු විට, ඔබට ඉහළ සීමාවක් හමු විය. එවිට ඔබට සම්මත ද්විමය සෙවීමක් කළ හැකිය.


ඇත්ත වශයෙන්ම කාලය අවම වශයෙන් O((log n)^2)ගුණනය නියත-කාල නොවන නමුත් ඇත්ත වශයෙන්ම අඩු සීමාවක් ඇති O(log n)බැවින් විශාල බහු නිරවද්‍යතා සංඛ්‍යා සමඟ වැඩ කරන විට එය පැහැදිලි වේ. නමුත් මෙම විකියේ විෂය පථය බිටු 64 ක් බව පෙනේ, එබැවින් එය එන්බීඩී විය හැකිය.

10

මාටිනස්ගේ විසඳුම පහත දැක්වෙන සරල කිරීම මඟින් ධාවන කාලයෙන් සියයට කිහිපයක් රැවුල කපන බවක් පෙනේ, නමුත් මට විශ්වාස කළ හැකි මිණුම් දණ්ඩක් නිපදවීමට මිණුම් සලකුණු කිරීමේදී මම ප්‍රමාණවත් නොවේ:

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
ප්‍රති results ල මෙහි ඇත. පළමු පරීක්ෂණය ඉවත් කිරීම නරක ය, මන්ද එය බොහෝ අවස්ථාවන් ලාභදායී ලෙස විසඳන බැවිනි. මූලාශ්‍රය මගේ පිළිතුරෙහි ඇත (යාවත්කාලීන කර ඇත).
maaartinus

9

කාර්ය සාධනය සඳහා, ඔබට බොහෝ විට යම් යම් සම්මුතීන් කිරීමට සිදුවේ. තවත් සමහරු විවිධ ක්‍රම ප්‍රකාශ කර ඇත, කෙසේ වෙතත්, ඔබ සඳහන් කළේ කාර්මාක්ගේ හැක් එන් හි ඇතැම් අගයන්ට වඩා වේගවත් බවය. එවිට ඔබ "එන්" පරික්ෂා කළ යුතු අතර එය එම අංකයට වඩා අඩු නම් කාර්මාක්ගේ හැක් භාවිතා කරන්න, නැතිනම් විස්තර කර ඇති වෙනත් ක්‍රමයක් භාවිතා කරන්න මෙහි පිළිතුරු වලින්.


මම ඔබේ යෝජනාව ද විසඳුමට ඇතුළත් කර ඇත්තෙමි. ඒ වගේම ලස්සන හසුරුව. :)
Kip

8

මෙම ත්‍රෙඩ් එකේ අනෙක් අය විසින් යෝජනා කරන ලද ශිල්පීය ක්‍රම භාවිතා කරමින් මට ඉදිරිපත් කළ හැකි වේගවත්ම ජාවා ක්‍රියාත්මක කිරීම මෙයයි.

  • මොඩ් -256 පරීක්ෂණය
  • නිරවද්‍ය මොඩ් -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;
        }
    }

}

7

ඔබ මුල සිටම N හි 2-බල කොටස ඉවත් කළ යුතුය.

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

ප්‍රොජෙක්ට් අයිලර් ටැග් වල සඳහන් කර ඇති අතර එහි ඇති බොහෝ ගැටලු පරීක්ෂා කිරීමේ අංක >> අවශ්‍ය වේ 2^64. ඔබ බයිට් 80 බෆරයක් සමඟ වැඩ කරන විට ඉහත සඳහන් කළ බොහෝ ප්‍රශස්තිකරණ පහසුවෙන් ක්‍රියා නොකරයි.

මම ජාවා බිග් ඉන්ටෙගර් සහ නිව්ටන්ගේ ක්‍රමයේ තරමක් වෙනස් කළ අනුවාදයක් භාවිතා කළෙමි. ගැටළුව වූයේ ඒ වෙනුවට නිශ්චිත චතුරස්රයන් n^2අභිසාරී වීමයි(n-1)n නිසාn^2-1 = (n-1)(n+1) සහ අවසන් දෝෂයක් අවසන් භාජකය එක් පියවරක් පහත වූ අතර ඇල්ගොරිතම අවසන්. දෝෂය ගණනය කිරීමට පෙර මුල් තර්කයට එකක් එකතු කිරීමෙන් එය නිවැරදි කිරීම පහසුය. (Ube නක මුල් සඳහා දෙකක් එකතු කරන්න)

මෙම ඇල්ගොරිතමයේ එක් හොඳ ලක්ෂණයක් නම්, එම අංකය පරිපූර්ණ චතුරස්රයක් දැයි ඔබට වහාම පැවසිය හැකිය - නිව්ටන්ගේ ක්රමයේ අවසාන දෝෂය (නිවැරදි කිරීම නොවේ) ශුන්ය වනු ඇත. සරල වෙනස් කිරීමකින් ඔබට 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 සඳහා බොහෝ දේ ඉතිරි කර දෙනු ඇතැයි මම සැක කරමි. නොමිලේ. චක්‍ර 15 බැගින් පුනරාවර්තන දුසිමක්, එය සේදීමකි. තවමත්, සිත්ගන්නාසුළු වීමට +1.
තඩ්මාස්

එකතු කිරීම් සහ අඩු කිරීම් XOR මගින් කළ හැකි බව මම විශ්වාස කරමි.
බ්‍රෙන්ට්.ලෝන්ග්බරෝ

එය ඩෆ්ට් අදහස් දැක්වීමක් විය - එකතු කිරීම පමණක් කළ හැක්කේ XOR ය; අඩු කිරීම අංක ගණිතය.
බ්‍රෙන්ට්.ලොංබරෝ

1
කෙසේ වෙතත් XOR හි ධාවන කාලය සහ එකතු කිරීම අතර සැලකිය යුතු වෙනසක් තිබේද?
තඩ්මාස්

1
Ad ටැඩ්මාස්: “පසුව ප්‍රශස්තිකරණය” රීතිය බිඳ දැමීමට බොහෝ විට ප්‍රමාණවත් නොවේ. (:-)
බ්‍රෙන්ට්.ලෝන්ග්බරෝ

6

සඳහන් කර ඇති පරිදි වර්ග ඇමතුම හරියටම නිවැරදි නැත, නමුත් එය වේගය අනුව අනෙක් පිළිතුරු ඉවතට නොයෑම සිත්ගන්නාසුළු හා උපදේශාත්මක ය. සියල්ලට පසු, වර්ග අඩි සඳහා එකලස් කිරීමේ භාෂා උපදෙස් අනුක්‍රමය ඉතා කුඩාය. ඉන්ටෙල් සතුව දෘඩාංග උපදෙස් ඇත, එය ජාවා විසින් භාවිතා නොකරන අතර එය අයිඊඊඊයට අනුකූල නොවන නිසා.

ඉතින් මන්දගාමී වන්නේ ඇයි? ජාවා ඇත්ත වශයෙන්ම ජේඑන්අයි හරහා සී පුරුද්දක් ලෙස හඳුන්වන අතර, එය ජාවා සබ්මැරීනයක් ඇමතීමට වඩා එසේ කිරීම මන්දගාමී වන අතර එය පේළිගත කිරීමට වඩා මන්දගාමී වේ. මෙය ඉතා කරදරකාරී වන අතර, ජාවා වඩා හොඳ විසඳුමක් ඉදිරිපත් කළ යුතුව තිබුණි, එනම් අවශ්‍ය නම් පාවෙන ස්ථාන පුස්තකාල ඇමතුම් තැනීම. හා හොඳයි.

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

මගේ හරය 2 යුගල යන්ත්‍රයේ වේලාවෙන් 1/3 ක් පමණ ක්‍රියාත්මක වන්නේ පර්ෆෙක්ට්ස්ක්වෙයාර් 5 ය. එකම රේඛා ඔස්සේ තවත් කරකැවීම් සාමාන්‍යයෙන් කාලය තවදුරටත් අඩු කර ගත හැකි යැයි මම සැක කරමි, නමුත් ඔබ පරීක්ෂා කරන සෑම අවස්ථාවකම, ඔබ තව දුරටත් තුරන් කිරීම සඳහා වැඩි පරීක්‍ෂණයක් සිදු කරයි, එබැවින් ඔබට එම මාර්ගයේ වැඩි දුරක් යා නොහැක.

නිසැකවම, negative ණ සඳහා වෙනම පරීක්ෂණයක් කරනවාට වඩා, ඔබට ඉහළ බිටු 6 එකම ආකාරයකින් පරීක්ෂා කළ හැකිය.

මා කරමින් සිටින්නේ හැකි චතුරස්රයන් ඉවත් කිරීම පමණක් බව සලකන්න, නමුත් මට විභව නඩුවක් ඇති විට මුල් පිටපත ඇමතීමට සිදුවන්නේ පර්පෙෆ්ට්ස්ක්වෙයාර් ය.

Pp1 සහ pp2 හි ස්ථිතික අගයන් ආරම්භ කිරීම සඳහා init2 චර්යාව එක් වරක් කැඳවනු ලැබේ. 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 සඳහා සරල හා පැහැදිලිව පරීක්ෂා කිරීම අවශ්‍ය වේ. පහත දැක්වෙන සරල අර්ථයෙන් එය දැනට පවතින ගණිත ශ්‍රිත භාවිතා කිරීමට උත්සාහ නොකරයි. බොහෝ ක්‍රියාකරු බිට් නැණවත් ක්‍රියාකරුවන් සමඟ ප්‍රතිස්ථාපනය කළ හැකිය. මම කිසිදු බංකුවක් සලකුණු දත්තයකින් පරීක්ෂා කර නැත. මම විශේෂයෙන් ගණිතය හෝ පරිගණක ඇල්ගොරිතම නිර්මාණය පිළිබඳ විශේෂ expert යෙක් නොවෙමි, ඔබ ගැටලුව පෙන්වා දෙනවා දැකීමට මම කැමතියි. එහි වැඩි දියුණු කිරීමේ අවස්ථා රාශියක් ඇති බව මම දනිමි.

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

IpKip: මගේ බ්‍රව්සරයේ යම් ගැටළුවක්.
nabam serbang

1
ඔබට ඉන්ඩෙන්ටින් ටිකක් අවශ්‍යයි.
ස්ටීව් කුඕ

5

වර්ගයක අවසාන බිටු n නිරීක්ෂණය කළ විට ඇති විය හැකි සියලු ප්‍රති results ල මම පරීක්ෂා කළෙමි. තවත් බිටු අනුපිළිවෙලින් පරීක්ෂා කිරීමෙන්, යෙදවුම් 5/6 දක්වා ඉවත් කළ හැකිය. මම ඇත්ත වශයෙන්ම මෙය නිර්මාණය කර ඇත්තේ ෆර්මැට්ගේ සාධකකරණ ඇල්ගොරිතම ක්‍රියාත්මක කිරීම සඳහා වන අතර එය එහි ඉතා වේගවත්ය.

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

ව්‍යාජ කේතයේ අවසාන කොටස වැඩි අගයන් තුරන් කිරීම සඳහා පරීක්ෂණ දීර් extend කිරීමට භාවිතා කළ හැකිය. ඉහත පරීක්ෂණ k = 0, 1, 2, 3 සඳහා වේ

  • a ස්වරූපයෙන් (3 << 2k) - 1
  • b ස්වරූපයෙන් වේ (2 << 2k)
  • c ස්වරූපයෙන් (2 << 2k + 2) - 1
  • d ස්වරූපයෙන් වේ (2 << 2k - 1) * 10

    එය මුලින් බලගන්වන්නේ එහි බලයේ දෙකක මාපාංකයක් සහිත චතුරස්රාකාර අවශේෂයක් තිබේද යන්නයි, පසුව එය අවසාන මොඩියුලයක් මත පදනම්ව පරීක්ෂා කරයි, පසුව එය Math.sqrt භාවිතා කර අවසාන පරීක්ෂණයක් සිදු කරයි. මම ඉහළ තනතුරේ සිට අදහස ඉදිරිපත් කර එය මත දිගු කිරීමට උත්සාහ කළෙමි. ඕනෑම අදහස් හෝ යෝජනා මම අගය කරමි.

    යාවත්කාලීන කිරීම: මොඩියුලස්, (මොඩ්ස්ක්) සහ 44352 ක මාපාංක පදනමක් මගින් පරීක්ෂණය භාවිතා කරමින්, මගේ පරීක්ෂණය OP හි යාවත්කාලීනයේ 1,000,000,000 දක්වා සංඛ්‍යා සඳහා 96% ක කාලයක් තුළ ක්‍රියාත්මක වේ.


  • 2

    මෙන්න බෙදීම් සහ ජය ගැනීමේ විසඳුමක්.

    ස්වාභාවික සංඛ්‍යාවක වර්ග මූලය ( 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"

    2
    මම සංකල්පයට කැමතියි, නමුත් මම ප්‍රධාන අඩුපාඩුවක් ආචාරශීලී ලෙස පෙන්වා දීමට කැමැත්තෙමි: සංඛ්‍යා පදනම් 2 ද්විමය වේ. toStringබිට්වේස් ක්‍රියාකරුවන්ට සාපේක්ෂව බේස් 2 බේස් 10 බවට පරිවර්තනය කිරීම ඇදහිය නොහැකි තරම් මිල අධික ක්‍රියාවකි. මේ අනුව, ප්‍රශ්නයේ පරමාර්ථය තෘප්තිමත් කිරීම සඳහා - කාර්ය සාධනය - ඔබ පදනම් 10 නූල් වෙනුවට බිට්වේස් ක්‍රියාකරුවන් භාවිතා කළ යුතුය. නැවතත්, මම ඇත්තටම ඔබේ සංකල්පයට කැමතියි. එසේ වුවද, ඔබේ ක්‍රියාත්මක කිරීම (එය දැන් පවතින ආකාරයට) ප්‍රශ්නය සඳහා පළ කළ හැකි සියලු විසඳුම් වලින් මන්දගාමී වේ.
    ජැක් ගිෆින්

    1

    වේගය සැලකිලිමත් වන්නේ නම්, බහුලව භාවිතා වන යෙදවුම් කට්ටලය සහ ඒවායේ අගයන් විමසුම් වගුවකට බෙදා නොගෙන සුවිශේෂී අවස්ථා සඳහා ඔබ ඉදිරිපත් කර ඇති ඕනෑම ප්‍රශස්ත මැජික් ඇල්ගොරිතමයක් කරන්නේ ඇයි?


    ගැටළුව වන්නේ "බහුලව භාවිතා වන යෙදවුම් කට්ටලයක්" නොමැති වීමයි - සාමාන්‍යයෙන් මම ලැයිස්තුවක් හරහා නැවත කියමි, එබැවින් මම එකම යෙදවුම් දෙවරක් භාවිතා නොකරමි.
    කිප්

    1

    'අවසාන X ඉලක්කම් N' නම් ඊට වඩා කාර්යක්ෂමව 'පරිපූර්ණ චතුරස්රයක් විය නොහැක' ඇසුරුම් කිරීමට හැකි විය යුතුය! මම ජාවා 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("};");
    }

    ප්‍රති the ල මෙන්න:

    (සංස්කරණය: 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 වැනි උපදෙස් ලබා දෙන්නේ නම්, එය ප්‍රමුඛ ශුන්‍යයන් ගණනය කරන අතර සැලකිය යුතු බිටු වලින් අඩක් සමඟ ආරම්භක අගයක් ලබා ගැනීමට අපට එය භාවිතා කළ හැකිය. සෑම විටම ප්‍රමාණවත් වන නිව්ටන් පියවර ගණන නිශ්චිත සංඛ්‍යාවක් අපට ප්‍රවේශමෙන් සොයාගත හැකිය. (මේ අනුව, ලූප් කිරීමේ අවශ්‍යතාවය සහ ඉතා ඉක්මණින් ක්‍රියාත්මක කිරීම.)

    දෙවන විසඳුම වන්නේ පාවෙන ලක්ෂ්‍ය පහසුකම හරහා වන අතර එය වේගවත් වර්ග ගණනය කිරීමක් කළ හැකිය (i87 කොප්‍රොසෙසරය වැනි.) එක්ස්පෝ () සහ ලොග් () හරහා විනෝද චාරිකාවක් පවා නිව්ටන් ද්විමය සෙවුමකට පිරිහීමට වඩා වේගවත් විය හැකිය. මේ සඳහා උපායශීලී පැතිකඩක් ඇත, පසුව පිරිපහදු කිරීම අවශ්‍ය වන්නේ කුමක් ද යන්න පිළිබඳ සකසනය මත රඳා පවතින විශ්ලේෂණයක්.

    තුන්වන විසඳුම තරමක් වෙනස් ගැටළුවක් විසඳන නමුත් ප්‍රශ්නයෙහි තත්වය විස්තර කර ඇති නිසා සඳහන් කිරීම වටී. සුළු වශයෙන් වෙනස් වන සංඛ්‍යා සඳහා වර්ග මූලයන් විශාල ප්‍රමාණයක් ගණනය කිරීමට ඔබට අවශ්‍ය නම්, ඔබට කිසි විටෙකත් ආරම්භක අගය නැවත ආරම්භ නොකරන්නේ නම්, ඔබට නිව්ටන් පුනරාවර්තනය භාවිතා කළ හැකිය, නමුත් පෙර ගණනය කිරීම අතහැර දැමූ තැනින් එය තබන්න. මම මෙය අවම වශයෙන් එක් අයිලර් ගැටලුවක සාර්ථකව භාවිතා කර ඇත්තෙමි.


    හොඳ තක්සේරුවක් ලබා ගැනීම එතරම් අපහසු නොවේ. විසඳුම සඳහා පහළ සහ ඉහළ මායිමක් තක්සේරු කිරීමට ඔබට අංකයේ ඉලක්කම් ගණන භාවිතා කළ හැකිය. බෙදීමක් සහ ජය ගැනීමේ විසඳුමක් මා යෝජනා කරන මගේ පිළිතුරද බලන්න.
    එම්ඩබ්ලිව්බී

    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. (ගණිතය ටිකක් පහසු කිරීම සඳහා මම සං bit ා බිට් නොසලකා හරිනවා ... ඇත්ත වශයෙන්ම (දිගු) (2 ^ 31.5) = 3037000499 පරිපූර්ණ චතුරස්රයන් ඇත)
    Kip

    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;

    නිසැකවම, එක් එක් මුරපොලකදී කොපමණ සංඛ්‍යාවක් තෝරාගෙන ඇත්දැයි දැන ගැනීම සිත්ගන්නාසුළු වනු ඇත - චෙක්පත් සැබවින්ම ස්වාධීනද යන්න සැක සහිතය.

    By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
    Licensed under cc by-sa 3.0 with attribution required.