බිටු 32 ක පූර්ණ සංඛ්‍යාවක් තුළ සැකසූ බිට් ගණන ගණනය කරන්නේ කෙසේද?


883

අංක 7 නියෝජනය කරන බිටු 8 ක් මේ වගේ ය:

00000111

බිටු තුනක් සකසා ඇත.

බිටු 32 ක පූර්ණ සංඛ්‍යාවක් තුළ ඇති කට්ටල බිටු ගණන තීරණය කිරීම සඳහා ඇල්ගොරිතම මොනවාද?


102
මෙය හැමිං බර BTW වේ.
Purfideas

11
මේ සඳහා සැබෑ ලෝක යෙදුමක් කුමක්ද? (මෙය විවේචනයක් ලෙස ගත යුතු නොවේ - මම කුතුහලයෙන්
සිටිමි

8
සන්නිවේදනයේ සරල දෝෂ හඳුනාගැනීමක් ලෙස භාවිතා කළ සමාන්තර බිට් ගණනය කිරීම (එය සොයා බලන්න).
ඩයලෙක්ටිකස්

8
Ia ඩයෙලෙක්ටිකස්, හැමිං බර ගණනය කිරීමට වඩා සමානාත්මතාවය ගණනය කිරීම ලාභදායී වේ
finnw

15
ospookyjon අපි ඔබට ප්‍රස්ථාරයක් යාබද අනුකෘතියක් ලෙස නිරූපණය කර ඇති බව කියමු. ඔබට පෘෂ් te වංශියක දාර ගණන ගණනය කිරීමට අවශ්‍ය නම්, එය බිට් කට්ටලයේ එක් පේළියක හැමිං බර ගණනය කිරීමට තම්බා ගනී.
fuz

Answers:


864

මෙය ' හැමිං බර ', 'පොප්කවුන්ට්' හෝ 'පසෙකට එකතු කිරීම' ලෙස හැඳින්වේ.

'හොඳම' ඇල්ගොරිතම සැබවින්ම රඳා පවතින්නේ ඔබ සිටින්නේ කුමන CPU මතද යන්න සහ ඔබේ භාවිත රටාව කුමක්ද යන්න මතය.

සමහර CPU වලට එය කිරීමට තනි නිමැවුම් උපදෙස් ඇති අතර අනෙක් ඒවාට සමාන්තර උපදෙස් ඇත, ඒවා බිට් දෛශික මත ක්‍රියා කරයි. සමාන්තර උපදෙස් (x86 වැනි popcnt, එය සහාය දක්වන CPU වල) නිසැකවම පාහේ වේගවත් වනු ඇත. තවත් සමහර ගෘහ නිර්මාණ ශිල්පයට මයික්‍රෝ කේත ලූපයක් සමඟ මන්දගාමී උපදෙස් ක්‍රියාත්මක කළ හැකි අතර එය එක් චක්‍රයකට මඳක් පරීක්ෂා කරයි ( උපුටා දැක්වීම අවශ්‍ය වේ ).

ඔබේ CPU හි විශාල හැඹිලියක් තිබේ නම් සහ / හෝ ඔබ මෙම උපදෙස් බොහොමයක් තද පුඩුවක් තුළ කරන්නේ නම් පූර්ව ජනගහනය සහිත වගු බැලීමේ ක්‍රමයක් ඉතා වේගවත් විය හැකිය. කෙසේවෙතත්, 'හැඹිලි මිස්' හි වියදම නිසා එය දුක් විඳිය හැකිය, එහිදී CPU විසින් ප්‍රධාන මතකයෙන් වගු කිහිපයක් ලබා ගත යුතුය. (මේසය කුඩා ලෙස තබා ගැනීම සඳහා එක් එක් බයිටය වෙන වෙනම බලන්න.)

ඔබේ බයිට් බොහෝ විට 0 හෝ 1 ට වඩා වැඩි වනු ඇති බව ඔබ දන්නේ නම්, මෙම අවස්ථා සඳහා ඉතා කාර්යක්ෂම ඇල්ගොරිතම ඇත.

'සමාන්තර' හෝ 'විචල්‍ය-නිරවද්‍ය SWAR ඇල්ගොරිතම' ලෙස හැඳින්වෙන ඉතා හොඳ පොදු අරමුණු ඇල්ගොරිතමයක් පහත දැක්වෙන බව මම විශ්වාස කරමි. මම මෙය සී වැනි ව්‍යාජ භාෂාවෙන් ප්‍රකාශ කර ඇත්තෙමි, ඔබට එය විශේෂිත භාෂාවක් සඳහා වැඩ කිරීමට අවශ්‍ය විය හැකිය (උදා: සී ++ සඳහා uint32_t සහ ජාවා හි >>> භාවිතා කිරීම):

int numberOfSetBits(uint32_t i)
{
     // Java: use int, and use >>> instead of >>
     // C or C++: use uint32_t
     i = i - ((i >> 1) & 0x55555555);
     i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
     return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}

ජාවාස්ක්‍රිප්ට් සඳහා: කාර්ය සාධනය සඳහා පූර්ණ සංඛ්‍යා සමඟ බල කිරීම|0 : පළමු පේළිය වෙනස් කරන්නi = (i|0) - ((i >> 1) & 0x55555555);

සාකච්ඡා කරන ලද ඕනෑම ඇල්ගොරිතමයක හොඳම නරකම හැසිරීම මෙයයි, එබැවින් ඔබ එය විසි කරන ඕනෑම භාවිත රටාවක් හෝ අගයන් සමඟ කාර්යක්ෂමව කටයුතු කරනු ඇත.


මෙම SWAR බිටාක් ක්‍රියා කරන ආකාරය:

i = i - ((i >> 1) & 0x55555555);

පළමු පියවර වන්නේ අමුතු / පවා බිටු හුදකලා කිරීම, ඒවා පෙළගැස්වීමට මාරුවීම සහ එකතු කිරීම සඳහා වෙස්මුහුණු කිරීමේ ප්‍රශස්ත අනුවාදයකි. මෙය effectively ලදායි ලෙස ද්වි -බිටු සමුච්චය තුළ වෙනම එකතු කිරීම් 16 ක් සිදු කරයි ( ලේඛනයක් තුළ SWAR = SIMD ). වගේ (i & 0x55555555) + ((i>>1) & 0x55555555).

මීලඟ පියවර එම 16x 2-bit සමුච්චකාරකවල අමුතු / අටක් ගෙන නැවත 8x 4-bit එකතුවක් නිපදවයි. මෙම අවස්ථාවේදී i - ...ප්‍රශස්තිකරණය කළ නොහැකි බැවින් එය මාරුවීමට පෙර / පසු ආවරණ කරයි. මාරුවීමට පෙර 0x33...වෙනුවට එකම නියතය දෙවරක් භාවිතා 0xccc...කිරීම අයිඑස්ඒ සඳහා සම්පාදනය කිරීමේදී වෙන වෙනම ලේඛනයේ බිටු 32 නියතයන් සෑදිය යුතුය.

(i + (i >> 4)) & 0x0F0F0F0F4x 8-bit සමුච්චය දක්වා පුළුල් කිරීමේ අවසාන මාරුව සහ එකතු කිරීමේ පියවර . අනුරූප ආදාන බිටු වල බිටු 4 ම සකසා ඇත්නම්, එය පෙර වෙනුවට වෙනුවට එකතු කිරීමෙන් පසුව ආවරණය 4කරයි. 4 + 4 = 8 එය තවමත් බිටු 4 කට ගැලපේ, එබැවින් නිබ්බල් මූලද්‍රව්‍ය අතර ගෙන යා නොහැක i + (i >> 4).

මෙතෙක් මෙය තරමක් සාමාන්‍ය සිම්පතක් වන අතර දක්ෂ ප්‍රශස්තිකරණ කිහිපයක් සහිත SWAR ශිල්පීය ක්‍රම භාවිතා කරයි. තවත් පියවර 2 ක් සඳහා එකම රටාවකින් ඉදිරියට යාම 2x 16-bit දක්වා පුළුල් කළ හැකි අතර 1x 32-bit ගණන් කළ හැකිය. නමුත් වේගවත් දෘඩාංග ගුණනය සහිත යන්ත්‍රවල වඩාත් කාර්යක්ෂම ක්‍රමයක් ඇත:

අපට ප්‍රමාණවත් “මූලද්‍රව්‍ය” කිහිපයක් ඇති විට, මැජික් නියතයක් සමඟ ගුණ කිරීමෙන් සියලු මූලද්‍රව්‍යයන් ඉහළ මූලද්‍රව්‍යයට එකතු කළ හැකිය. මෙම අවස්ථාවේ දී බයිට් මූලද්රව්ය. ගුණ කිරීම සිදු කරනු ලබන්නේ වම් මාරුව සහ එකතු කිරීමෙනි, එබැවින් ප්‍රති results ල ගුණ කිරීම . x * 0x01010101x + (x<<8) + (x<<16) + (x<<24) අපගේ බිටු 8 මූලද්‍රව්‍යයන් ප්‍රමාණවත් තරම් පුළුල් වන අතර (ප්‍රමාණවත් තරම් කුඩා ප්‍රමාණයක් තබාගෙන) මෙම ඉහළම බිටු 8 තුළට ගෙන නොයයි .

මෙහි 64-බිට් අනුවාදයකට 0x0101010101010101 ගුණකයක් සහිත 64-බිටු නිඛිලයක 8x 8-bit මූලද්‍රව්‍ය කළ හැකි අතර ඉහළ බයිට් සමඟ උපුටා ගන්න >>56. එබැවින් එය අතිරේක පියවරයන් නොගනී, පුළුල් නියතයන් පමණි. __builtin_popcountllදෘඩාංග popcntඋපදෙස් සක්‍රිය කර නොමැති විට xCC පද්ධති සඳහා GCC භාවිතා කරන්නේ මෙයයි. මේ සඳහා ඔබට බිල්ඩින් හෝ සහජයෙන්ම භාවිතා කළ හැකි නම්, ඉලක්කගත විශේෂිත ප්‍රශස්තිකරණ සිදු කිරීමට සම්පාදකයාට අවස්ථාවක් ලබා දෙන්න.


පුළුල් දෛශික සඳහා සම්පූර්ණ සිම්ඩ් සමඟ (උදා: මුළු අරාව ගණනය කිරීම)

මෙම බිට්වේස්-එස්ඩබ්ලිව්ආර් ඇල්ගොරිතමය සමාන්තරගත කළ හැක්කේ එක් පූර්ණ සංඛ්‍යා ලේඛනයක් වෙනුවට එකවර දෛශික මූලද්‍රව්‍ය කිහිපයකින් සිදු කිරීමට ය. (උදා: x86-64 කේතය නෙහෙලම් හෝ පසුව පමණක් නොව ඕනෑම CPU මත ධාවනය කළ යුතුය.)

කෙසේ වෙතත්, පොප්කවුන්ට් සඳහා දෛශික උපදෙස් භාවිතා කිරීමට ඇති හොඳම ක්‍රමය නම් සාමාන්‍යයෙන් විචල්ය-මාරු කිරීමක් භාවිතා කරමින් එක් එක් බයිට් එකකට සමාන්තරව බිටු 4 ක් සඳහා වගු බැලීමක් කිරීමයි. (බිටු 4 දර්ශකය දෛශික ලේඛනයේ තබා ඇති 16 ප්‍රවේශ වගුවක්).

Intel CPUs හි, දෘඩාංග 64bit popcnt උපදෙස් මඟින් SSSE3 PSHUFBබිට්-සමාන්තර ක්‍රියාත්මක කිරීම 2 ක සාධකයකින් ඉක්මවා යා හැක , නමුත් ඔබේ සම්පාදකයා එය නිවැරදිව ලබා ගන්නේ නම් පමණි . එසේ නොමැතිනම් SSE සැලකිය යුතු ලෙස ඉදිරියට පැමිණිය හැකිය. නව සම්පාදක අනුවාදයන් ඉන්ටෙල් හි පොප්කන්ට් ව්‍යාජ පරායත්තතා ගැටලුව පිළිබඳව දැනුවත් ය .

යොමුව:


89
හා! NumberOfSetBits () ශ්‍රිතයට ආදරය කරන්න, නමුත් කේත සමාලෝචනයක් හරහා එය ලබා ගැනීම වාසනාවකි. :-)
ජේසන් එස්

37
සමහර විට එය භාවිතා කළ යුතුය unsigned int, එය කිසිදු සං bit ා බිට් සංකූලතා වලින් තොර බව පහසුවෙන් පෙන්වීමට. එසේම uint32_tසියලු වේදිකාවලින් ඔබ අපේක්ෂා කරන දේ ඔබට ආරක්ෂිතද?
ක්‍රේග් මැක්වීන්

35
@nonnb: ඇත්ත වශයෙන්ම, ලියා ඇති පරිදි, කේතය දෝෂ සහිත වන අතර නඩත්තු කිරීම අවශ්‍ය වේ. >>negative ණ අගයන් සඳහා ක්‍රියාත්මක කිරීම-අර්ථ දක්වා ඇත. තර්කය වෙනස් කිරීමට (හෝ වාත්තු කිරීමට) අවශ්‍ය unsignedවන අතර කේතය 32-බිටු-විශේෂිත බැවින් එය බොහෝ විට භාවිතා කළ යුතුය uint32_t.
ආර් .. GitHub STOP HELPING ICE

6
එය ඇත්තෙන්ම මැජික් නොවේ. එය බිටු කට්ටල එකතු කරන නමුත් සමහර දක්ෂ ප්‍රශස්තිකරණයන් සමඟ එසේ කරයි. පිළිතුරෙහි දක්වා ඇති විකිපීඩියා සබැඳිය සිදුවන්නේ කුමක්ද යන්න පැහැදිලි කිරීමේ හොඳ කාර්යයක් ඉටු කරයි, නමුත් මම රේඛාව අනුව යන්නෙමි. 1) සෑම බිටු යුගලයකම බිටු ගණන ගණන් කරන්න, එම ගණන එම බිටු යුගලයට දමන්න (ඔබට 00, 01, හෝ 10 ක් ඇත); මෙහි ඇති “දක්ෂ” බිට් එක වන්නේ එක් වෙස් මුහුණක් මග හැරීමයි. 2) එම බිට්පේයර් යුගලවල යුගල ඒවාට අනුරූප නිබ්ල්ස් වලට එක් කරන්න; මෙහි දක්ෂ කිසිවක් නැත, නමුත් සෑම නිබ්ල් එකකටම දැන් 0-4 අගයක් ඇත. (cont'd)
dash-tom-bang

8
තවත් සටහනක්, මෙය නියතයන් නිසි ලෙස විස්තාරණය කිරීමෙන් බිට් 64 සහ 128 බිට් රෙජිස්ටර් දක්වා විහිදේ. සිත්ගන්නා කරුණ නම් (මට නම්) එම නියතයන් ~ 0/3, 5, 17 සහ 255 වේ; කලින් තිදෙනා 2 ^ n + 1 වේ. මේ සියල්ල ඔබ වඩාත් උනන්දුවෙන් බලා සිටින අතර ස්නානය කිරීමේදී ඒ ගැන සිතන්න. :)
dash-tom-bang

218

ඔබේ සම්පාදකයින්ගේ සාදන ලද කාර්යයන් ද සලකා බලන්න.

උදාහරණයක් ලෙස ග්නූ සම්පාදකයේ ඔබට භාවිතා කළ හැකිය:

int __builtin_popcount (unsigned int x);
int __builtin_popcountll (unsigned long long x);

නරකම අවස්ථාවකදී සම්පාදකයා ශ්‍රිතයකට ඇමතුමක් ජනනය කරයි. හොඳම අවස්ථාවේ දී සම්පාදකයා එකම කාර්යය වේගයෙන් සිදු කිරීම සඳහා cpu උපදෙස් විමෝචනය කරයි.

ජීසීසී අභ්‍යන්තරය විවිධ වේදිකා හරහා පවා ක්‍රියාත්මක වේ. X86 ගෘහ නිර්මාණ ශිල්පයේ පොප්කවුන්ට් ප්‍රධාන ධාරාව බවට පත්වනු ඇත, එබැවින් දැන් සහජයෙන්ම භාවිතා කිරීම ආරම්භ කිරීම අර්ථවත් කරයි. වෙනත් ගෘහ නිර්මාණ ශිල්පයට වසර ගණනාවක් තිස්සේ පොප්කවුන්ට් ඇත.


X86 මත, ඔබ ඒ සඳහා සහයෝගය භාර ගත නොහැකි බව සම්පාදකවරයා කියන්න පුළුවන් popcntසමග උපදෙස් -mpopcntහෝ -msse4.2ද එම පරම්පරාව තුළ එකතු කරන ලද දෛශික උපදෙස් සක්රීය කිරීම. GCC x86 විකල්ප බලන්න . -march=nehalem(හෝ -march=ඔබේ කේතය උපකල්පනය කර සුසර කිරීමට ඔබට අවශ්‍ය ඕනෑම CPU) හොඳ තේරීමක් විය හැකිය. එහි ප්‍රති ing ලයක් ලෙස ද්විමය පැරණි CPU මත ධාවනය කිරීමෙන් නීති විරෝධී උපදෙස් දෝෂයක් ඇති වේ.

ඔබ ඒවා සාදන යන්ත්‍රය සඳහා ද්විමය ප්‍රශස්තිකරණය කිරීමට, භාවිතා කරන්න -march=native (gcc, clang, හෝ ICC සමඟ).

MSVC x86 popcntඋපදෙස් සඳහා සහජයෙන්ම සපයයි , නමුත් gcc මෙන් නොව එය සැබවින්ම දෘඩාංග උපදෙස් සඳහා සහජයෙන්ම වන අතර දෘඩාංග සහාය අවශ්‍ය වේ.


std::bitset<>::count()සාදන ලද එකක් වෙනුවට භාවිතා කිරීම

න්‍යායට අනුව, ඉලක්කගත CPU සඳහා කාර්යක්ෂමව පොප් ගණනය කරන්නේ කෙසේදැයි දන්නා ඕනෑම සම්පාදකයෙකු එම ක්‍රියාකාරිත්වය ISO C ++ හරහා හෙළි කළ යුතුය std::bitset<>. ප්‍රායෝගිකව, සමහර ඉලක්කගත CPU සඳහා සමහර අවස්ථාවල ඔබ bit-hack AND / shift / ADD සමඟ වඩා හොඳ විය හැකිය.

දෘඩාංග පොප්කවුන්ට් යනු විකල්ප දිගුවක් (x86 වැනි) ඉලක්කගත ගෘහ නිර්මාණ සඳහා, සෑම සම්පාදකයෙකුටම std::bitsetලබා ගත හැකි විට එයින් ප්‍රයෝජන ගත හැකි නොවේ. නිදසුනක් ලෙස, popcntසම්පාදනය කරන වේලාවේදී MSVC හට සහය සක්‍රීය කිරීමට ක්‍රමයක් නොමැති අතර (SSE4.2 යන්නෙන් ගම්‍ය වේ, තාක්‍ෂණිකව වෙනම විශේෂාංග බිට් එකක් තිබුණද) සෑම විටම වගු විමසුමක් භාවිතා කරයි ./Ox /arch:AVXpopcnt

නමුත් අවම වශයෙන් ඔබට සෑම තැනකම ක්‍රියා කළ හැකි අතේ ගෙන යා හැකි යමක් ලැබෙනු ඇති අතර නිවැරදි ඉලක්ක විකල්ප සමඟ gcc / clang සමඟින් ඔබට සහාය දක්වන ගෘහ නිර්මාණ සඳහා දෘඩාංග පොප්කවුන්ට් ලැබේ.

#include <bitset>
#include <limits>
#include <type_traits>

template<typename T>
//static inline  // static if you want to compile with -mpopcnt in one compilation unit but not others
typename std::enable_if<std::is_integral<T>::value,  unsigned >::type 
popcount(T x)
{
    static_assert(std::numeric_limits<T>::radix == 2, "non-binary type");

    // sizeof(x)*CHAR_BIT
    constexpr int bitwidth = std::numeric_limits<T>::digits + std::numeric_limits<T>::is_signed;
    // std::bitset constructor was only unsigned long before C++11.  Beware if porting to C++03
    static_assert(bitwidth <= std::numeric_limits<unsigned long long>::digits, "arg too wide for std::bitset() constructor");

    typedef typename std::make_unsigned<T>::type UT;        // probably not needed, bitset width chops after sign-extension

    std::bitset<bitwidth> bs( static_cast<UT>(x) );
    return bs.count();
}

ගොඩ්බෝල්ට් සම්පාදක ගවේෂකයේ gcc, clang, icc, සහ MSVC වෙතින් asm බලන්න .

x86-64 gcc -O3 -std=gnu++11 -mpopcntමෙය විමෝචනය කරයි:

unsigned test_short(short a) { return popcount(a); }
    movzx   eax, di      # note zero-extension, not sign-extension
    popcnt  rax, rax
    ret
unsigned test_int(int a) { return popcount(a); }
    mov     eax, edi
    popcnt  rax, rax
    ret
unsigned test_u64(unsigned long long a) { return popcount(a); }
    xor     eax, eax     # gcc avoids false dependencies for Intel CPUs
    popcnt  rax, rdi
    ret

PowerPC64 gcc -O3 -std=gnu++11විමෝචනය ( ආග් intඅනුවාදය සඳහා):

    rldicl 3,3,0,32     # zero-extend from 32 to 64-bit
    popcntd 3,3         # popcount
    blr

මෙම ප්‍රභවය x86- විශේෂිත හෝ GNU- විශේෂිත නොවේ, නමුත් x86 සඳහා gcc / clang / icc සමඟ පමණක් සම්පාදනය කරයි.

තනි උපදෙස් පොප්කවුන්ට් නොමැතිව ගෘහ නිර්මාණ ශිල්පය සඳහා gcc හි පසුබෑම බයිට්-ඇට්-ටයිම් මේස විමසුමකි. උදාහරණයක් ලෙස ARM සඳහා මෙය අපූරු නොවේ .


5
පොදුවේ මෙය හොඳ පුහුණුවක් බව මම එකඟ වෙමි, නමුත් XCode / OSX / Intel හි මෙහි පළ කර ඇති බොහෝ යෝජනා වලට වඩා මන්දගාමී කේතයක් ජනනය කරන බව මට පෙනී ගියේය. විස්තර සඳහා මගේ පිළිතුර බලන්න.

5
Intel i5 / i7 හි SSE4 උපදෙස් POPCNT ඇත, එය පොදු අරමුණු ලේඛණ භාවිතා කරයි. මගේ පද්ධතියේ ඇති GCC මෙම අභ්‍යන්තරය භාවිතා කරමින් එම උපදෙස් විමෝචනය නොකරයි, මම හිතන්නේ තවමත් -march = nehalem විකල්පයක් නොමැති නිසාය.
matja

3
@matja, මම -msse4.2 සමඟ සම්පාදනය කරන්නේ නම් මගේ GCC 4.4.1 පොප්කන්ට් උපදෙස් විමෝචනය කරයි
Nils Pipenbrinck

75
c ++ භාවිතා කරන්න std::bitset::count. මෙය එක් __builtin_popcountඇමතුමකට සම්පාදනය කිරීමෙන් පසුව .
deft_code

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

191

මගේ මතය අනුව, “හොඳම” විසඳුම වන්නේ වෙනත් ක්‍රමලේඛකයෙකුට (හෝ වසර දෙකකට පසුව මුල් ක්‍රමලේඛකයාට) විශාල අදහස් දැක්වීමකින් තොරව කියවිය හැකි විසඳුමයි. සමහරු දැනටමත් ලබා දී ඇති වේගවත්ම හෝ දක්ෂතම විසඳුම ඔබට අවශ්‍ය විය හැකි නමුත් ඕනෑම වේලාවක දක්ෂතාවයට වඩා කියවීමේ හැකියාව මම කැමැත්තෙමි.

unsigned int bitCount (unsigned int value) {
    unsigned int count = 0;
    while (value > 0) {           // until all bits are zero
        if ((value & 1) == 1)     // check lower bit
            count++;
        value >>= 1;              // shift bits, removing lower bit
    }
    return count;
}

ඔබට වැඩි වේගයක් අවශ්‍ය නම් (සහ ඔබේ අනුප්‍රාප්තිකයන්ට උපකාර කිරීම සඳහා ඔබ එය හොඳින් ලේඛනගත කර ඇතැයි උපකල්පනය කළහොත්), ඔබට මේස බැලීමක් භාවිතා කළ හැකිය:

// Lookup table for fast calculation of bits set in 8-bit unsigned char.

static unsigned char oneBitsInUChar[] = {
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F (<- n)
//  =====================================================
    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, // 0n
    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 1n
    : : :
    4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, // Fn
};

// Function for fast calculation of bits set in 16-bit unsigned short.

unsigned char oneBitsInUShort (unsigned short x) {
    return oneBitsInUChar [x >>    8]
         + oneBitsInUChar [x &  0xff];
}

// Function for fast calculation of bits set in 32-bit unsigned int.

unsigned char oneBitsInUInt (unsigned int x) {
    return oneBitsInUShort (x >>     16)
         + oneBitsInUShort (x &  0xffff);
}

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


22
2 න් බෙදීම සහ එය "මාරුව බිටු ..." ලෙස අදහස් දැක්වීම වෙනුවට, ඔබ කළ යුත්තේ මාරුව ක්‍රියාකරු (>>) භාවිතා කර අදහස් දැක්වීමෙන් ඉවත් වන්න.
indiv

9
එය විස්ථාපනය කිරීමට වඩා තේරුමක් නැහැ if ((value & 1) == 1) { count++; }සමග count += value & 1?
පොන්කඩූඩ්ල්

21
නැත, හොඳම විසඳුම මෙම නඩුවේ වඩාත්ම කියවිය හැකි විසඳුම නොවේ. මෙන්න හොඳම ඇල්ගොරිතම වේගවත්ම එකයි.
NikiC

22
එය සම්පූර්ණයෙන්ම ඔබේ මතයයි, iknikic, ඔබට මාව පහත් කොට සැලකීමට නිදහස තිබුණත්, පැහැදිලිවම. "හොඳම" ගණනය කරන්නේ කෙසේද යන ප්‍රශ්නයේ කිසිදු සඳහනක් නොතිබුණි, "කාර්ය සාධනය" හෝ "වේගවත්" යන වචන කොතැනකවත් දැකිය නොහැක. ඒ නිසයි මම කියවිය හැකි දේ තෝරා ගත්තේ.
paxdiablo

3
මම මෙම පිළිතුර වසර 3 කට පසුව කියවන අතර එය හොඳම පිළිතුර ලෙස මා දකින්නේ එය කියවිය හැකි සහ වැඩි අදහස් ඇති නිසාය. කාලය.
waka-waka-waka

97

හැකර්ස් ඩයිලයිට් වෙතින්, පි. 66, රූපය 5-2

int pop(unsigned x)
{
    x = x - ((x >> 1) & 0x55555555);
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
    x = (x + (x >> 4)) & 0x0F0F0F0F;
    x = x + (x >> 8);
    x = x + (x >> 16);
    return x & 0x0000003F;
}

~ 20-ඊෂ් උපදෙස් වලින් ක්‍රියාත්මක වේ (ආරුක්කු මත රඳා පවතී), අතු නැත.

හැකර් ගේ ප්රීති වේ ප්රීතිමත්! බෙහෙවින් නිර්දේශිතයි.


8
ජාවා ක්‍රමය Integer.bitCount(int)මෙයම හරියටම ක්‍රියාත්මක කරයි.
මාකෝ බොලිස්

මෙය අනුගමනය කිරීමේදී සුළු කරදරයක් තිබීම - අපි බිට් 32 වෙනුවට බිට් 16 අගයන් ගැන පමණක් සැලකිලිමත් වන්නේ නම් එය වෙනස් වන්නේ කෙසේද?
ජෙරමි බ්ලම්

සමහර විට හැකර්වරුන්ගේ ප්‍රමෝදය ප්‍රියජනකයි, නමුත් මම මේ popවෙනුවට එය හඳුන්වන ඕනෑම කෙනෙකුට හොඳ පයින් ගසමි population_count(හෝ pop_cntඔබට සංක්ෂිප්තයක් තිබිය යුතුය). Ar මාකෝබොලිස් ජාවා හි සියලුම සංස්කරණ සම්බන්ධයෙන් එය සත්‍යයක් වනු ඇතැයි මම සිතමි, නමුත් නිල වශයෙන් එය ක්‍රියාත්මක කිරීම මත රඳා පවතී :)
මාර්ටන් බොඩෙව්ස්

තවද, පිළිගත් පිළිතුරේ කේතය වැනි ගුණ කිරීම අවශ්‍ය නොවේ.
ඇලෙක්ස්

බිට් 64 ට සාමාන්‍යකරණය කිරීමේදී ගැටලුවක් ඇති බව සලකන්න. වෙස් මුහුණ නිසා ප්‍රති result ලය 64 විය නොහැක.
ඇල්බට් වැන් ඩර් හෝස්ට්

76

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

int popcount(int v) {
    v = v - ((v >> 1) & 0x55555555);                // put count of each 2 bits into those 2 bits
    v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // put count of each 4 bits into those 4 bits  
    return c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
}

එය ක්‍රියාත්මක වන්නේ ඔබට කොටස් දෙකකින් බෙදීමෙන්, කොටස් දෙකේම ඇති කට්ටල ගණන ගණනය කිරීමෙන් පසුව ඒවා එකතු කිරීමෙන් ඔබට මුළු කට්ටල ගණන ගණනය කළ හැකි බැවිනි. Divide and Conquerආදර්ශයක් ලෙස ද දැන ගන්න . අපි විස්තරාත්මකව බලමු ..

v = v - ((v >> 1) & 0x55555555); 

බිටු දෙකක බිටු ගණන විය හැකිය 0b00, 0b01හෝ 0b10. මෙය බිටු 2 කින් ක්‍රියාත්මක කිරීමට උත්සාහ කරමු ..

 ---------------------------------------------
 |   v    |   (v >> 1) & 0b0101   |  v - x   |
 ---------------------------------------------
   0b00           0b00               0b00   
   0b01           0b00               0b01     
   0b10           0b01               0b01
   0b11           0b01               0b10

අවශ්‍ය වූයේ මෙයයි: අවසාන තීරුවේ සෑම බිට් යුගල දෙකකම කට්ටල බිටු ගණන පෙන්වයි. දෙක ටිකක් අංකය නම් >= 2 (0b10)එවිට andනිෂ්පාදනය 0b01, වෙන නිෂ්පාදනය 0b00.

v = (v & 0x33333333) + ((v >> 2) & 0x33333333); 

මෙම ප්රකාශය තේරුම් ගැනීමට පහසු විය යුතුය. පළමු මෙහෙයුමෙන් පසු සෑම බිටු දෙකකම කට්ටල බිටු ගණන අප සතුව ඇත, දැන් අපි එම අගය සෑම බිටු 4 කින්ම සාරාංශ කරමු.

v & 0b00110011         //masks out even two bits
(v >> 2) & 0b00110011  // masks out odd two bits

ඉන්පසු අපි ඉහත ප්‍රති result ලය සාරාංශ කොට, බිට් 4 කින් සකස් කළ බිට් ගණන ගණනය කරමු. අන්තිම ප්රකාශය වඩාත් උපක්රමශීලී ය.

c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;

අපි එය තවදුරටත් බිඳ දමමු ...

v + (v >> 4)

එය දෙවන ප්‍රකාශයට සමාන ය; අපි ඒ වෙනුවට 4 කාණ්ඩවල කට්ටල බිටු ගණන් කරමු. අපි දන්නවා our අපගේ පෙර මෙහෙයුම් නිසා every සෑම නිබ්බයකම එහි සැකසූ බිටු ගණන ඇති බව. උදාහරණයක් බලමු. අපිට බයිට් තියෙනවා යැයි සිතමු 0b01000010. එහි අර්ථය වන්නේ පළමු නිබ්ල්ට එහි 4bit කට්ටලයක් ඇති අතර දෙවැන්න එහි 2bit කට්ටලයක් ඇත. දැන් අපි එම නිබ්බල් එකට එකතු කරමු.

0b01000010 + 0b01000000

එය අපට පළමු නයිබලය තුළ බයිට් එකක සැකසූ බිටු ගණන ලබා දෙයි, 0b01100010එබැවින් අපි අංකයේ ඇති සියලුම බයිට් වල අවසාන බයිට් හතර ආවරණය කරමු (ඒවා ඉවතලමින්).

0b01100010 & 0xF0 = 0b01100000

දැන් සෑම බයිටයකම එහි කට්ටල බිටු ගණනක් ඇත. අපි ඒවා සියල්ලම එකතු කළ යුතුයි. උපක්‍රමය නම් 0b10101010සිත්ගන්නාසුලු දේපලක් ඇති ප්‍රති result ලය ගුණ කිරීමයි . අපගේ අංකයට බයිට් හතරක් තිබේ නම් A B C D, එය මෙම බයිට් සමඟ නව අංකයක් ලබා දෙනු A+B+C+D B+C+D C+D Dඇත. 4 බයිට් අංකයකට උපරිම බිටු 32 ක් තිබිය හැකි අතර එය නිරූපණය කළ හැකිය 0b00100000.

අපට දැන් අවශ්‍ය වන්නේ සියලුම බයිට් වල ඇති සියලුම බිටු වල එකතුව ඇති පළමු බයිටය වන අතර, අපි එය ලබා ගනිමු >> 24. මෙම ඇල්ගොරිතම 32 bitවචන සඳහා නිර්මාණය කර ඇති නමුත් වචන සඳහා පහසුවෙන් වෙනස් කළ හැකිය 64 bit.


කුමක් c = ගැන ද? පෙනෙන ආකාරයට ඉවත් කළ යුතුය. තවද, සමහර සම්භාව්‍ය අනතුරු ඇඟවීම් වලක්වා ගැනීම සඳහා A "(((v + (v >> 4)) සහ 0xF0F0F0F) * 0x1010101) >> 24" යන අමතර පරෙවියන් කට්ටලයක් යෝජනා කරන්න.
chux - මොනිකා නැවත ස්ථාපනය කරන්න

4
වැදගත් ලක්ෂණය නම් මෙම 32-බිටු පුරුද්ද popcount(int v)සහ දෙකම සඳහා ක්‍රියා කිරීමයි popcount(unsigned v). popcount(uint32_t v)අතේ ගෙන යාහැකි , සලකා බලන්න , ආදිය. ඇත්ත වශයෙන්ම * 0x1010101 කොටසට කැමතියි.
chux - මොනිකා නැවත ස්ථාපනය කරන්න

සෝස්? (පොත, සබැඳිය, ඉන්වෙටර්වරුන්ගේ නම් ආදිය) ඉතා සාදරයෙන් පිළිගනී. මක්නිසාද යත්, එය අපගේ කේත කඳවුරුවල එය පැමිණෙන්නේ කොතැනට දැයි අදහස් දැක්වීමක් සමඟ ඇලවිය හැකිය.
v.oddou

1
වඩා හොඳ පැහැදිලි කිරීමක් සඳහා අවසාන පේළිය මෙසේ ලිවිය යුතු යැයි මම සිතමි: return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;එබැවින් ඔබ සැබවින්ම කරන්නේ කුමක්දැයි බැලීමට අපට ලිපි ගණන් කිරීම අවශ්‍ය නොවේ (ඔබ පළමුවැන්න ඉවත දැමූ බැවින්, ඔබ 0වැරදීමකින් (පෙරළා දැමූ) බිට් රටාව වෙස්මුහුණක් ලෙස භාවිතා කර ඇතැයි මම සිතුවෙමි. - එනම් මා සඳහන් කරන තෙක් ඇත්තේ අක්ෂර 7 ක් මිස 8 ක් නොවේ).
emem

ගුණ 0x01010101 විසින් සකසනය මත පදනම්ව, මන්දගාමී විය හැක. උදාහරණයක් ලෙස, මගේ පැරණි පවර්බුක් ජී 4 හි 1 ගුණ කිරීම එකතු කිරීම් 4 ක් තරම් මන්දගාමී විය (බෙදීම තරම් නරක නැත, එහිදී 1 බෙදීම එකතු කිරීම් 23 ක් තරම් මන්දගාමී විය).
ජෝර්ජ් කොහෙලර්

55

මම කම්මැලියි, සහ ප්‍රවේශ තුනක පුනරාවර්තන බිලියනයක් ගත කළෙමි. සම්පාදකයා gcc -O3 වේ. CPU යනු ඔවුන් 1 වන මැක්බුක් ප්‍රෝ හි තබන ඕනෑම දෙයකි.

තත්පර 3.7 කින් වේගවත්ම දේ පහත දැක්වේ:

static unsigned char wordbits[65536] = { bitcounts of ints between 0 and 65535 };
static int popcount( unsigned int i )
{
    return( wordbits[i&0xFFFF] + wordbits[i>>16] );
}

දෙවන ස්ථානය එකම කේතයකට ගියද අර්ධ වචන 2 වෙනුවට බයිට් 4 ක් ඉහළට ඔසවයි. ඒ සඳහා තත්පර 5.5 ක් පමණ ගත විය.

තෙවන ස්ථානය තත්පර 8.6 ක් ගත වූ බිට්-ට්විඩ්ලිං 'පැති දෙපස එකතු කිරීම' ප්‍රවේශයට යයි.

සිව්වන ස්ථානය ජීසීසී හි __builtin_popcount () වෙත ලජ්ජා සහගත තත්පර 11 කින් ගමන් කරයි.

එක් වරකට ගණන් කිරීමේ ප්‍රවේශය ඉතා මන්දගාමී වූ අතර එය අවසන් වන තෙක් බලා සිටීම මට කම්මැලි විය.

එබැවින් ඔබ සියල්ලටම වඩා කාර්ය සාධනය ගැන සැලකිලිමත් වන්නේ නම් පළමු ප්‍රවේශය භාවිතා කරන්න. ඔබ සැලකිලිමත් නමුත් 64Kb RAM ප්‍රමාණයක් ඒ සඳහා වැය කිරීමට ප්‍රමාණවත් නොවේ නම්, දෙවන ප්‍රවේශය භාවිතා කරන්න. එසේ නොමැතිනම් කියවිය හැකි (නමුත් මන්දගාමී) එක්-වරකට ප්‍රවේශයක් භාවිතා කරන්න.

ඔබට බිටු විකෘති කිරීමේ ප්‍රවේශය භාවිතා කිරීමට අවශ්‍ය තත්වයක් ගැන සිතීම දුෂ්කර ය.

සංස්කරණය කරන්න: සමාන ප්‍රති results ල මෙහි ඇත.


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

10
ඔබ මෙම චර්යාව මිලියන කිහිප වතාවක් තද පුඩුවක් ලෙස නොකියන්නේ නම්, එහි ක්‍රියාකාරිත්වය ගැන කිසිසේත් සැලකිලිමත් වීමට ඔබට හේතුවක් නැත, එසේම කාර්ය සාධනය නැතිවීම නොසැලකිලිමත් වන බැවින් බොළඳ නමුත් කියවිය හැකි ප්‍රවේශය භාවිතා කළ හැකිය. FWIW, 8bit LUT ඇමතුම් 10-20 ක් තුළ හැඹිලි උණුසුම් කරයි.

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

ක්ලැන්ග් සමඟ මෙය උත්සාහ කරන්න, එය බිල්ඩින් ක්‍රියාත්මක කිරීමේදී සැලකිය යුතු ලෙස දක්ෂයි.
මැට් ජොයිනර්

3
-Msese4.2 සමඟ ඇමතුමක් ලබා නොගතහොත් GCC විසින් පොප්කොන්ට් උපදෙස් විමෝචනය නොකරනු ඇත.
lvella

55

ඔබ ජාවා භාවිතා කරන්නේ නම්, සාදන ලද ක්‍රමය Integer.bitCountඑය කරයි.


සූර්යයා විවිධ ඒපීඅයි ලබා දුන් විට, එය පසුබිම පිළිබඳ යම් තර්කනයක් භාවිතා කළ යුතුය, නේද?
වල්ලබ් පටඩේ

2
පැත්තක නෝට්ටු ලෙස, ජාවා ගේ ක්රියාත්මක කිරීම භාවිතා එම ඇල්ගොරිතමය විසින් පෙන්වා කෙවින් ලිට්ල් .
මාකෝ බොලිස්

2
ක්‍රියාත්මක කිරීම පසෙකට දැමුවහොත්, මෙය ඔබට පසුව ඔබේ කේතය නඩත්තු කරන සංවර්ධකයින්ගේ පැහැදිලි පණිවුඩය විය හැකිය (හෝ ඔබ මාස 6 කට පසුව නැවත එය වෙත පැමිණෙන විට)
divillysausages

31
unsigned int count_bit(unsigned int x)
{
  x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
  x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
  x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
  x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF);
  x = (x & 0x0000FFFF) + ((x >> 16)& 0x0000FFFF);
  return x;
}

මෙම ඇල්ගොරිතම පැහැදිලි කිරීමට මට ඉඩ දෙන්න.

මෙම ඇල්ගොරිතම පදනම් වී ඇත්තේ බෙදීම සහ යටත් කිරීමේ ඇල්ගොරිතම මත ය. 8bit නිඛිල 213 (ද්විමය 11010101) තිබේ යැයි සිතමු, ඇල්ගොරිතම මේ ආකාරයට ක්‍රියා කරයි (සෑම අවස්ථාවකම අසල්වැසි කොටස් දෙකක් ඒකාබද්ධ කරයි):

+-------------------------------+
| 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |  <- x
|  1 0  |  0 1  |  0 1  |  0 1  |  <- first time merge
|    0 0 1 1    |    0 0 1 0    |  <- second time merge
|        0 0 0 0 0 1 0 1        |  <- third time ( answer = 00000101 = 5)
+-------------------------------+

7
මෙම ඇල්ගොරිතමය කියවිය නොහැකි තත්වයට පත්වීමට පෙර මැට් හොවෙල්ස් විසින් පළ කරන ලද අනුවාදය වේ.
Lefteris E

29

මෙය ඔබේ ක්ෂුද්‍ර ගෘහ නිර්මාණ ශිල්පය දැන ගැනීමට උපකාරී වන ප්‍රශ්න වලින් එකකි. ක්‍රියාකාරී ඇමතුම් උඩිස්, පුනරාවර්තන බිලියනයක් ඉවත් කිරීම සඳහා සී ++ ඉන්ලයින් භාවිතා කරමින් -ඕ 3 සමඟ සම්පාදනය කරන ලද ජීසීසී 4.3.3 යටතේ ප්‍රභේද දෙකක් මා විසින් කාලානුරූපව සකසා ඇති අතර, සම්පාදකයා වැදගත් කිසිවක් ඉවත් නොකරන බව සහතික කිරීම සඳහා සියලු ගණන්වල ධාවන එකතුව තබා ගනිමින්, වේලාව සඳහා rdtsc භාවිතා කරමින් ( ඔරලෝසු චක්‍රය හරියටම).

inline int pop2 (අත්සන් නොකළ x, අත්සන් නොකළ y)
{
    x = x - ((x >> 1) & 0x55555555);
    y = y - ((y >> 1) & 0x55555555);
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
    y = (y & 0x33333333) + ((y >> 2) & 0x33333333);
    x = (x + (x >> 4)) & 0x0F0F0F0F;
    y = (y + (y >> 4)) & 0x0F0F0F0F;
    x = x + (x >> 8);
    y = y + (y >> 8);
    x = x + (x >> 16);
    y = y + (y >> 16);
    ආපසු (x + y) & 0x000000FF;
}

නවීකරණය නොකළ හැකර්ස් ඩයිලයිට් ගිගාසයිල් 12.2 ක් ගත්තේය. මගේ සමාන්තර අනුවාදය (බිටු මෙන් දෙගුණයක් ගණන් කිරීම) ගිගාසයිල් 13.0 කින් ධාවනය වේ. 2.4GHz Core Duo එකකින් දෙකම සඳහා 10.5s එකතුව. ගිගාසයිල් 25 ක් = මෙම ඔරලෝසු සංඛ්‍යාතයට තත්පර 10 කට වඩා වැඩිය, එබැවින් මගේ වේලාව නිවැරදි බව මට විශ්වාසයි.

මෙම ඇල්ගොරිතම සඳහා ඉතා නරක වන උපදෙස් පරායත්ත දාමයන් සමඟ මෙය සම්බන්ධ වේ. බිට් 64 රෙජිස්ටර් යුගලයක් භාවිතා කිරීමෙන් මට නැවත වේගය දෙගුණයක් කළ හැකිය. ඇත්ත වශයෙන්ම, මම දක්ෂ නම් සහ x + y ටිකක් එකතු කළහොත් මට මාරුවීම් කිහිපයක් කපා දැමිය හැකිය. 64-බිට් අනුවාදය කුඩා කරකැවීම් කිහිපයක් සමඟ පවා එළියට එනු ඇත, නමුත් නැවත බිටු මෙන් දෙගුණයක් ගණන් කරන්න.

බිට් 128 සිම්ඩ් රෙජිස්ටර් සමඟ, තවත් සාධක දෙකක් වන අතර, එස්එස්ඊ උපදෙස් කට්ටලවල බොහෝ විට දක්ෂ කෙටි කප්පාදුවක් ද ඇත.

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

හරි, මම තීරණය කළා නවීකරණය කරන ලද 64-බිට් අනුවාදය. මේ සඳහා එක් ප්‍රමාණයක් (අත්සන් නොකල දිගු) == 8

inline int pop2 (අත්සන් නොකළ දිගු x, අත්සන් නොකළ දිගු y)
{
    x = x - ((x >> 1) & 0x5555555555555555);
    y = y - ((y >> 1) & 0x5555555555555555);
    x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333);
    y = (y & 0x3333333333333333) + ((y >> 2) & 0x3333333333333333);
    x = (x + (x >> 4)) & 0x0F0F0F0F0F0F0F0F;
    y = (y + (y >> 4)) & 0x0F0F0F0F0F0F0F0F;
    x = x + y; 
    x = x + (x >> 8);
    x = x + (x >> 16);
    x = x + (x >> 32); 
    ආපසු x & 0xFF;
}

එය හරි යැයි පෙනේ (මම පරිස්සමින් පරික්ෂා නොකරමි). දැන් වේලාව ගිගාසයිල් 10.70 / ගිගාසයිල් 14.1 ට එළියට එයි. එම පසුකාලීන සංඛ්‍යාව බිට් බිලියන 128 ක් වූ අතර මෙම යන්ත්‍රයේ ගතවූ 5.9 ට අනුරූප වේ. සමාන්තර නොවන අනුවාදය ඉතා සුළු වේගයකින් වේගවත් වන නිසා මම 64-බිට් මාදිලියේ ධාවනය වන අතර එය බිට් 64 රෙජිස්ටරයට බිට් 32 රෙජිස්ටරයට වඩා තරමක් හොඳය.

මෙහි OOO නල මාර්ගයක් තව ටිකක් තිබේදැයි බලමු. මෙය තව ටිකක් සම්බන්ධ වූ නිසා මම ඇත්ත වශයෙන්ම ටිකක් පරීක්ෂා කළෙමි. එක් එක් පදය පමණක් 64 ක් වන අතර සියල්ල එකතුව 256 කි.

inline int pop4 (අත්සන් නොකළ දිගු x, අත්සන් නොකළ දිගු y, 
                අත්සන් නොකළ දිගු u, අත්සන් නොකළ දිගු v)
{
  enum {m1 = 0x5555555555555555, 
         m2 = 0x3333333333333333, 
         m3 = 0x0F0F0F0F0F0F0F0F, 
         m4 = 0x000000FF000000FF};

    x = x - ((x >> 1) & m1);
    y = y - ((y >> 1) & m1);
    u = u - ((u >> 1) & m1);
    v = v - ((v >> 1) & m1);
    x = (x & m2) + ((x >> 2) & m2);
    y = (y & m2) + ((y >> 2) & m2);
    u = (u & m2) + ((u >> 2) & m2);
    v = (v & m2) + ((v >> 2) & m2);
    x = x + y; 
    u = u + v; 
    x = (x & m3) + ((x >> 4) & m3);
    u = (u & m3) + ((u >> 4) & m3);
    x = x + u; 
    x = x + (x >> 8);
    x = x + (x >> 16);
    x = x & m4; 
    x = x + (x >> 32);
    ආපසු x & 0x000001FF;
}

මම මොහොතකට කලබල වූ නමුත් සමහර පරීක්ෂණ වලදී මම ඉන්ලයින් යතුරු පදය භාවිතා නොකලද gcc -O3 සමඟ පේළිගත උපක්‍රම සෙල්ලම් කරන බව පෙනේ. මම gcc සෙල්ලම් උපක්‍රම වලට ඉඩ දෙන විට, pop4 () වෙත බිලියනයක ඇමතුම් ගිගාසයිල් 12.56 ක් ගත වේ, නමුත් එය නියත ප්‍රකාශන ලෙස තර්ක නැවීම බව මම තීරණය කළෙමි. තවත් 30% වේගවත් කිරීම සඳහා වඩාත් යථාර්ථවාදී සංඛ්‍යාවක් 19.6gc ලෙස පෙනේ. මගේ ටෙස්ට් ලූපය දැන් මේ ආකාරයට පෙනේ, සෑම තර්කයක්ම gcc උපක්‍රම නැවැත්වීමට තරම් වෙනස් බව සහතික කර ගන්න.

   hitime b4 = rdtsc (); 
   සඳහා (අත්සන් නොකළ දිගු i = 10L * 1000 * 1000 * 1000; i <11L * 1000 * 1000 * 1000; ++ i) 
      sum + = pop4 (i, i ^ 1, ~ i, i | 1); 
   hitime e4 = rdtsc (); 

බිටු බිලියන 256 ක් 8.17s හි සාරාංශගත විය. බිට් 16 වගු බැලීමේදී මිණුම් සලකුණු කර ඇති පරිදි බිට් මිලියන 32 ක් සඳහා 1.02s දක්වා ක්‍රියා කරයි. කෙලින්ම සංසන්දනය කළ නොහැක, මන්ද අනෙක් බංකුව ඔරලෝසු වේගයක් ලබා නොදේ, නමුත් 64KB වගු සංස්කරණයෙන් මම කුඩා කැබැල්ල කපා දැමූ බවක් පෙනේ, එය L1 හැඹිලිය ඛේදජනක ලෙස භාවිතා කිරීමකි.

යාවත්කාලීන කිරීම: පැහැදිලිව පෙනෙන දේ කිරීමට තීරණය කර තවත් අනුපිටපත් පේළි හතරක් එකතු කර pop6 () නිර්මාණය කරන්න. ග්‍රෑම් 22.8 ක් දක්වා ඉහළ ගිය අතර, බිට් බිලියන 384 ක් 9.5s හි සාරාංශගත විය. ඉතින් තවත් 20% ක් දැන් බිට් බිලියන 32 ක් සඳහා 800ms දී ඇත.


2
මේ වගේ හොඳම එකලස් නොකරන ස්වරූපය මම වරකට නොකැඩූ වචන 32 32bit වචන දැක ඇත්තෙමි. dalkesciological.com/writings/diary/popcnt.c , stackoverflow.com/questions/3693981/… , dalkesciological.com/writings/diary/archive/2008/07/05/…
මැට්

29

නැවත 2 කින් බෙදන්නේ නැත්තේ ඇයි?

ගණන් = 0
අතර n> 0
  if (n% 2) == 1
    ගණන් + = 1
  n / = 2  

මෙය වේගවත්ම නොවන බව මම එකඟ වෙමි, නමුත් "හොඳම" තරමක් අපැහැදිලි ය. මම තර්ක කරන්නේ “හොඳම” යන්නට පැහැදිලි බවක් තිබිය යුතු බවයි


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

2
ඔබ මෙය කරන්නේ නම් මිස ගොඩක් , කාර්ය සාධනය බලපෑමක් නොසැලකිය යුතු වනු ඇත. එබැවින් සෑම දෙයක්ම එක හා සමාන වන අතර, ඩැනියෙල් සමඟ මම එකඟ වෙමි, 'හොඳම' යන්නෙන් අදහස් කරන්නේ "ගුප්ත ලෙස කියවන්නේ නැත" යන්නයි.

2
විවිධාකාර ක්‍රම ලබා ගැනීම සඳහා මම හිතාමතාම 'හොඳම' යන්න නිර්වචනය කර නැත. අපි මේ ආකාරයේ බිට්-ඇඹරීමේ මට්ටමට බැස ඇත්නම් එයට මුහුණ දෙමු. අපි බොහෝ විට චිම්පර් ටයිප් කර ඇති බවක් පෙනෙන uber-fast යමක් සොයනු ඇත.
මැට් හොවෙල්ස්

6
නරක කේතය. සම්පාදකයෙකු එයින් හොඳ එකක් සෑදිය හැකි නමුත් මගේ පරීක්ෂණ වලදී GCC එසේ නොවීය. (N% 2) (n & 1) සමඟ ප්‍රතිස්ථාපනය කරන්න; මොඩියුලෝ වලට වඩා වේගවත් වීම. (N / = 2) (n >> = 1) සමඟ ප්‍රතිස්ථාපනය කරන්න; බෙදීමට වඩා වේගයෙන් බිට්ෂිෆ්ට් කිරීම.
මැකී

6
@Mecki: මගේ පරීක්ෂණ දී, gcc (4.0, -O3) කළ පැහැදිලි optimisations කරන්න.

26

ඔබ බිට් රටා ලියන විට හැකර්ගේ ඩිලයිට් බිට්-ට්විඩ්ලිං වඩාත් පැහැදිලි වේ.

unsigned int bitCount(unsigned int x)
{
  x = ((x >> 1) & 0b01010101010101010101010101010101)
     + (x       & 0b01010101010101010101010101010101);
  x = ((x >> 2) & 0b00110011001100110011001100110011)
     + (x       & 0b00110011001100110011001100110011); 
  x = ((x >> 4) & 0b00001111000011110000111100001111)
     + (x       & 0b00001111000011110000111100001111); 
  x = ((x >> 8) & 0b00000000111111110000000011111111)
     + (x       & 0b00000000111111110000000011111111); 
  x = ((x >> 16)& 0b00000000000000001111111111111111)
     + (x       & 0b00000000000000001111111111111111); 
  return x;
}

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


3
මෙම විසඳුම ක්‍රියාකරුගේ ප්‍රමුඛතාවයට අදාළ සුළු ගැටළුවක් ඇති බව පෙනේ. එක් එක් පදය සඳහා එය මෙසේ පැවසිය යුතුය: x = (((x >> 1) & 0b0101010101010101010101010101) + (x & 0b01010101010101010101010101010101)); (එනම් අමතර වර්‍ග එකතු කරන ලදි).
නොපික්

22

2 32 බැලුම් වගුවක් අතර ප්‍රීතිමත් මාධ්‍යයක් සඳහා සහ එක් එක් බිටු හරහා තනි තනිව ක්‍රියා කිරීම:

int bitcount(unsigned int num){
    int count = 0;
    static int nibblebits[] =
        {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
    for(; num != 0; num >>= 4)
        count += nibblebits[num & 0x0f];
    return count;
}

Http://ctips.pbwiki.com/CountBits වෙතින්


අතේ ගෙන යා නොහැක. CPU හි බිට් බයිට් 9 ක් තිබේ නම් කුමක් කළ යුතුද? ඔව්, සැබෑ CPU එක එතන තියෙනවා ...
රොබට් එස්. බාන්ස්

16
O රොබට් එස්. බාන්ස්, මෙම ක්‍රියාව තවමත් ක්‍රියාත්මක වේ. එය ස්වදේශීය වචන ප්‍රමාණය ගැන උපකල්පනයක් නොකරන අතර “බයිට්” ගැන කිසිසේත් සඳහනක් නොකරයි.
finnw

මෙම කේතයේ සංකීර්ණතාව O(floor(log2(num))/4), numහැකි තරම් අත්තනෝමතික ලෙස විශාල විය හැකි යැයි උපකල්පනය කරන්නේද? මෙම නිසා whileක්රියාවලිය සඳහා nibble තියෙනවා තාක් කල් පුඩුවක් ක්? ඇත floor(log2(num))බිටු හා floor(log2(num)) / 4නිබලය. තර්කය නිවැරදිද?
රොබූර්_131

19

මෙය කළ හැක්කේ O(k), kබිට් ගණන සකසා ඇති ස්ථානයෙනි .

int NumberOfSetBits(int n)
{
    int count = 0;

    while (n){
        ++ count;
        n = (n - 1) & n;
    }

    return count;
}

මෙය අත්‍යවශ්‍යයෙන්ම බ්‍රයන් කර්නිගන්ගේ (ඔහුව මතකද?) ඇල්ගොරිතම n &= (n-1)වේ.
ඒඩ්‍රියන් මෝල්

17

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

unsigned int f(unsigned int x)
{
    switch (x) {
        case 0:
            return 0;
        case 1:
            return 1;
        case 2:
            return 1;
        case 3:
            return 2;
        default:
            return f(x/4) + f(x%4);
    }
}

4
ඔහ් මම ඒකට කැමතියි. කෙසේ ඕවරයේ පිඹුරා අනුවාදය:def f(i, d={0:lambda:0, 1:lambda:1, 2:lambda:1, 3:lambda:2}): return d.get(i, lambda: f(i//4) + f(i%4))()
underrun

10

ඔබ සොයන ශ්‍රිතය බොහෝ විට ද්විමය සංඛ්‍යාවක “පැත්තක එකතුව” හෝ “ජනගහන ගණන” ලෙස හැඳින්වේ. නුත් එය පූර්ව ෆැසිකල් 1 ඒ, පි 11-12 හි සාකච්ඡා කරයි (2 වන වෙළුමේ කෙටි සඳහනක් තිබුණද, 4.6.3- (7).)

මෙම ඉඩකඩක් classicus සිට පේදුරු Wegner ලිපිය "ද්විමය පරිගණක දී තාක්ෂණය ගණන් අය ගැන", යනු ද ඒ.සී.එම් සන්නිවේදන , වෙළුම 3 (1960) අංකය 5, 322 පිටුව . ඔහු එහිදී විවිධ ඇල්ගොරිතම දෙකක් ලබා දෙයි, එකක් “විරල” යැයි අපේක්ෂා කරන සංඛ්‍යා සඳහා ප්‍රශස්තිකරණය කර ඇත (එනම්, කුඩා සංඛ්‍යාවක් ඇත) සහ එකක් ප්‍රතිවිරුද්ධ අවස්ථාව සඳහා.


10
  private int get_bits_set(int v)
    {
      int c; // c accumulates the total bits set in v
        for (c = 0; v>0; c++)
        {
            v &= v - 1; // clear the least significant bit set
        }
        return c;
    }

9

විවෘත ප්‍රශ්න කිහිපයක්: -

  1. අංකය negative ණ නම්?
  2. අංකය 1024 නම්, “පුනරාවර්තන 2 කින් බෙදන්න” ක්‍රමය 10 වතාවක් පුනරාවර්තනය වේ.

පහත දැක්වෙන පරිදි negative ණ අංකයට සහය දැක්වීම සඳහා අපට ඇල්ගෝ වෙනස් කළ හැකිය: -

count = 0
while n != 0
if ((n % 2) == 1 || (n % 2) == -1
    count += 1
  n /= 2  
return count

දැන් දෙවන ගැටළුව මඟහරවා ගැනීම සඳහා අපට ඇල්ගෝව මෙසේ ලිවිය හැකිය: -

int bit_count(int num)
{
    int count=0;
    while(num)
    {
        num=(num)&(num-1);
        count++;
    }
    return count;
}

සම්පූර්ණ යොමු කිරීම සඳහා බලන්න:

http://goursaha.freeoda.com/Miscellaneous/IntegerBitCount.html


9

මම හිතන්නේ බ්‍රයන් කර්නිගන් ගේ ක්‍රමය ද ප්‍රයෝජනවත් වනු ඇත ... එය සැකසූ බිටු ඇති තරම් පුනරාවර්තන හරහා ගමන් කරයි. ඉතින් අපට ඉහළ බිට් කට්ටලයක් සහිත 32-බිට් වචනයක් තිබේ නම්, එය යන්නේ ලූපය හරහා එක් වරක් පමණි.

int countSetBits(unsigned int n) { 
    unsigned int n; // count the number of bits set in n
    unsigned int c; // c accumulates the total bits set in n
    for (c=0;n>0;n=n&(n-1)) c++; 
    return c; 
}

1988 දී ප්‍රකාශයට පත් කරන ලද සී ක්‍රමලේඛන භාෂාව 2 වන සංස්කරණය. (බ්‍රයන් ඩබ්ලිව්. කර්නිගන් සහ ඩෙනිස් එම්. රිචී විසින්) 2-9 ව්‍යායාමයේදී මෙය සඳහන් කරයි. 2006 අප්‍රියෙල් 19 වන දින ඩොන් නූත් මට පෙන්වා දුන්නේ මෙම ක්‍රමය "පීටර් වෙග්නර් විසින් CACM 3 (1960), 322 දී ප්‍රථම වරට ප්‍රකාශයට පත් කරන ලද බවයි.


8

මම පහත කේතය භාවිතා කරන්නේ එය වඩාත් බුද්ධිමත් ය.

int countSetBits(int n) {
    return !n ? 0 : 1 + countSetBits(n & (n-1));
}

තර්කනය: n & (n-1) අවසාන කට්ටලය n නැවත සකසයි.

PS: මෙය O (1) විසඳුමක් නොවන බව මම දනිමි.


අඩු බිටු සංඛ්‍යාවක් ඇති “විරල” අංක සඳහා මෙය හොඳ ය O(ONE-BITS). එය ඇත්ත වශයෙන්ම ඕ (1) වන බැවින් උපරිම බිටු 32 ක් ඇත.
ealfonso

7

"හොඳම ඇල්ගොරිතම" සමඟ ඔබ අදහස් කරන්නේ කුමක්ද? කෙටි කේතය හෝ නිරාහාර කේතය? ඔබේ කේතය ඉතා අලංකාර පෙනුමක් ඇති අතර එය නිරන්තරයෙන් ක්‍රියාත්මක කිරීමේ කාලයක් ඇත. කේතය ද ඉතා කෙටි ය.

නමුත් වේගය ප්‍රධාන සාධකය මිස කේත ප්‍රමාණය නොවේ නම් මම සිතන්නේ අනුගමනය කිරීම වේගවත් විය හැකිය:

       static final int[] BIT_COUNT = { 0, 1, 1, ... 256 values with a bitsize of a byte ... };
        static int bitCountOfByte( int value ){
            return BIT_COUNT[ value & 0xFF ];
        }

        static int bitCountOfInt( int value ){
            return bitCountOfByte( value ) 
                 + bitCountOfByte( value >> 8 ) 
                 + bitCountOfByte( value >> 16 ) 
                 + bitCountOfByte( value >> 24 );
        }

මම හිතන්නේ මෙය බිට් 64 ක අගයකට වඩා වේගවත් නොවනු ඇති නමුත් බිට් 32 ක අගයක් වේගවත් විය හැකිය.


මගේ කේතයට මෙහෙයුම් 10 ක් ඇත. ඔබේ කේතයට මෙහෙයුම් 12 ක් ඇත. ඔබගේ සබැඳිය කුඩා අරා (5) සමඟ ක්‍රියා කරයි. මම මූලද්රව්ය 256 ක් භාවිතා කරමි. හැඹිලිය සමඟ ගැටළුවක් විය හැකිය. නමුත් ඔබ එය නිතර භාවිතා කරන්නේ නම් මෙය ගැටළුවක් නොවේ.
Horcrux7

මෙම ප්‍රවේශය බිට්-ට්විඩ්ලිං ප්‍රවේශයට වඩා තරමක් වේගවත් ය. වැඩි මතකයක් භාවිතා කිරීම සඳහා, එය අඩු කේතයකට සම්පාදනය කරන අතර ඔබ ශ්‍රිතය පේළි කරන සෑම අවස්ථාවකම එම වාසිය නැවත නැවත සිදු වේ. එබැවින් එය පහසුවෙන් ශුද්ධ ජයග්‍රහණයක් බවට පත්විය හැකිය.

7

මම 1990 දී පමණ RISC යන්ත්‍ර සඳහා වේගවත් බිට්කවුන්ට් මැක්‍රෝවක් ලිව්වෙමි. එය උසස් ගණිතය (ගුණ කිරීම, බෙදීම,%), මතක ලබා ගැනීම (මාර්ගය ඉතා මන්දගාමී), ශාඛා (මාර්ගය ඉතා මන්දගාමී) භාවිතා නොකරයි, නමුත් එය උපකල්පනය කරන්නේ CPU සතුව 32-බිට් බැරල් මාරුව (වෙනත් වචන වලින් කිවහොත්, >> 1 සහ >> 32 එකම චක්‍ර ප්‍රමාණයක් ගනී.) කුඩා නියතයන්ට (6, 12, 24 වැනි) ලේඛනයට පැටවීමට කිසිවක් වැය නොවන බව හෝ ගබඩා කර ඇති බව උපකල්පනය කරයි. තාවකාලිකව සහ නැවත නැවත භාවිතා කරයි.

මෙම උපකල්පන සමඟ, එය බොහෝ RISC යන්ත්‍රවල චක්‍ර 16 ක / උපදෙස් වල බිටු 32 ක් ගණනය කරයි. උපදෙස් / චක්‍ර 15 ක් චක්‍ර හෝ උපදෙස් ගණනට වඩා අඩු සීමාවකට ආසන්න බව සලකන්න, මන්දයත් එකතු කිරීම් ගණන අඩකින් අඩු කිරීමට අවම වශයෙන් උපදෙස් 3 ක් (වෙස්, මාරුව, ක්‍රියාකරු) ගත යුතු බව පෙනේ, එබැවින් ලොග්_2 (32) = 5, 5 x 3 = 15 උපදෙස් අර්ධ-පහත් සීමාවකි.

#define BitCount(X,Y)           \
                Y = X - ((X >> 1) & 033333333333) - ((X >> 2) & 011111111111); \
                Y = ((Y + (Y >> 3)) & 030707070707); \
                Y =  (Y + (Y >> 6)); \
                Y = (Y + (Y >> 12) + (Y >> 24)) & 077;

පළමු හා වඩාත්ම සංකීර්ණ පියවර සඳහා රහසක් මෙන්න:

input output
AB    CD             Note
00    00             = AB
01    01             = AB
10    01             = AB - (A >> 1) & 0x1
11    10             = AB - (A >> 1) & 0x1

එබැවින් මම ඉහත 1 වන තීරුව (A) ගෙන, එය බිට් 1 ක් දකුණට ගෙන AB වෙතින් අඩු කළහොත්, මට ප්‍රතිදානය (CD) ලැබේ. බිටු 3 දක්වා දිගුව සමාන වේ; ඔබට අවශ්‍ය නම් මගේ වැනි පේළි 8 කින් යුත් බූලියන් වගුවකින් ඔබට එය පරීක්ෂා කළ හැකිය.

  • දොන් ගිලීස්

7

ඔබ C ++ භාවිතා කරන්නේ නම් තවත් විකල්පයක් වන්නේ අච්චු පරිවෘත්තීයකරණය භාවිතා කිරීමයි:

// recursive template to sum bits in an int
template <int BITS>
int countBits(int val) {
        // return the least significant bit plus the result of calling ourselves with
        // .. the shifted value
        return (val & 0x1) + countBits<BITS-1>(val >> 1);
}

// template specialisation to terminate the recursion when there's only one bit left
template<>
int countBits<1>(int val) {
        return val & 0x1;
}

භාවිතය වනුයේ:

// to count bits in a byte/char (this returns 8)
countBits<8>( 255 )

// another byte (this returns 7)
countBits<8>( 254 )

// counting bits in a word/short (this returns 1)
countBits<16>( 256 )

ඔබට විවිධ වර්ගවල (ස්වයංක්‍රීයව හඳුනාගැනීමේ බිට් ප්‍රමාණය පවා) භාවිතා කිරීමට මෙම අච්චුව තවදුරටත් පුළුල් කළ හැකි නමුත් පැහැදිලි කිරීම සඳහා මම එය සරලව තබා ගතිමි.

සංස්කරණය කරන්න: මෙය හොඳ යැයි සඳහන් කිරීමට අමතක වී ඇති බැවින් එය ඕනෑම C ++ සම්පාදකයක වැඩ කළ යුතු අතර එය මූලික වශයෙන් බිට් ගණන් කිරීම සඳහා නියත අගයක් භාවිතා කරන්නේ නම් එය ඔබේ ලූපය මුදා හරිනු ඇත (වෙනත් වචන වලින් කිවහොත්, එය වේගවත්ම සාමාන්‍ය ක්‍රමය බව මට හොඳටම විශ්වාසයි ඔබ සොයා ගනීවි)


අවාසනාවකට මෙන්, බිට් ගණන් කිරීම සමාන්තරව සිදු නොවේ, එබැවින් එය මන්දගාමී වේ. ලස්සන වෙන්නත් constexprපුළුවන්.
imallett

එකඟ විය - එය C ++ අච්චු පුනරාවර්තනයේ විනෝද අභ්‍යාසයක් විය, නමුත් නියත වශයෙන්ම ලස්සන බොළඳ විසඳුමකි.
පෙන්ටෆෝබ්

6

වාසනාවන්ත ගොනුවෙන් මෙම උදාහරණයට මම විශේෂයෙන් කැමතියි:

# BITCOUNT (x) නිර්වචනය කරන්න (((BX_ (x) + (BX_ (x) >> 4)) & 0x0F0F0F0F)% 255)
# BX_ (x) ((x) - (((x) >> 1) සහ 0x77777777) යන්න අර්ථ දක්වන්න
                             - (((x) >> 2) & 0x33333333)
                             - (((x) >> 3) & 0x11111111))

මම එයට වඩාත්ම කැමතියි එය ඉතා ලස්සන නිසා!


1
අනෙක් යෝජනා හා සසඳන විට එය ක්‍රියාත්මක වන්නේ කෙසේද?
asdf

6

ජාවා JDK1.5

Integer.bitCount (n);

මෙහි n යනු 1 ගණනය කළ යුතු අංකයයි.

එසේම පරීක්ෂා කරන්න,

Integer.highestOneBit(n);
Integer.lowestOneBit(n);
Integer.numberOfLeadingZeros(n);
Integer.numberOfTrailingZeros(n);

//Beginning with the value 1, rotate left 16 times
     n = 1;
         for (int i = 0; i < 16; i++) {
            n = Integer.rotateLeft(n, 1);
            System.out.println(n);
         }

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

2
සමහර ජාවා සංවර්ධකයින් මෙම ක්‍රමය ගැන නොදැන සිටිය හැකි නිසා en බෙන්සාඩෝ හරි නමුත් +1 කෙසේ වෙතත්
finnw

infinnw, මම ඒ සංවර්ධකයින්ගෙන් කෙනෙක්. :)
neevek

6

සිම්ඩ් උපදෙස් (SSSE3 සහ AVX2) භාවිතා කරමින් අරාව තුළ බිට් ගණන් කිරීම ක්‍රියාත්මක කිරීමක් මට හමු විය. එය __popcnt64 සහජ ශ්‍රිතය භාවිතා කරනවාට වඩා 2-2.5 ගුණයකින් හොඳ කාර්ය සාධනයක් ඇත.

SSSE3 අනුවාදය:

#include <smmintrin.h>
#include <stdint.h>

const __m128i Z = _mm_set1_epi8(0x0);
const __m128i F = _mm_set1_epi8(0xF);
//Vector with pre-calculated bit count:
const __m128i T = _mm_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);

uint64_t BitCount(const uint8_t * src, size_t size)
{
    __m128i _sum =  _mm128_setzero_si128();
    for (size_t i = 0; i < size; i += 16)
    {
        //load 16-byte vector
        __m128i _src = _mm_loadu_si128((__m128i*)(src + i));
        //get low 4 bit for every byte in vector
        __m128i lo = _mm_and_si128(_src, F);
        //sum precalculated value from T
        _sum = _mm_add_epi64(_sum, _mm_sad_epu8(Z, _mm_shuffle_epi8(T, lo)));
        //get high 4 bit for every byte in vector
        __m128i hi = _mm_and_si128(_mm_srli_epi16(_src, 4), F);
        //sum precalculated value from T
        _sum = _mm_add_epi64(_sum, _mm_sad_epu8(Z, _mm_shuffle_epi8(T, hi)));
    }
    uint64_t sum[2];
    _mm_storeu_si128((__m128i*)sum, _sum);
    return sum[0] + sum[1];
}

AVX2 අනුවාදය:

#include <immintrin.h>
#include <stdint.h>

const __m256i Z = _mm256_set1_epi8(0x0);
const __m256i F = _mm256_set1_epi8(0xF);
//Vector with pre-calculated bit count:
const __m256i T = _mm256_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 
                                   0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);

uint64_t BitCount(const uint8_t * src, size_t size)
{
    __m256i _sum =  _mm256_setzero_si256();
    for (size_t i = 0; i < size; i += 32)
    {
        //load 32-byte vector
        __m256i _src = _mm256_loadu_si256((__m256i*)(src + i));
        //get low 4 bit for every byte in vector
        __m256i lo = _mm256_and_si256(_src, F);
        //sum precalculated value from T
        _sum = _mm256_add_epi64(_sum, _mm256_sad_epu8(Z, _mm256_shuffle_epi8(T, lo)));
        //get high 4 bit for every byte in vector
        __m256i hi = _mm256_and_si256(_mm256_srli_epi16(_src, 4), F);
        //sum precalculated value from T
        _sum = _mm256_add_epi64(_sum, _mm256_sad_epu8(Z, _mm256_shuffle_epi8(T, hi)));
    }
    uint64_t sum[4];
    _mm256_storeu_si256((__m256i*)sum, _sum);
    return sum[0] + sum[1] + sum[2] + sum[3];
}

6

මම සෑම විටම මෙය තරඟකාරී ක්‍රමලේඛනයේදී භාවිතා කරන අතර එය ලිවීමට පහසු සහ කාර්යක්ෂම වේ:

#include <bits/stdc++.h>

using namespace std;

int countOnes(int n) {
    bitset<32> b(n);
    return b.count();
}

6

ඔබට කළ හැක්කේ එයයි

while(n){
    n=n&(n-1);
    count++;
}

මෙහි පිටුපස ඇති තර්කනය වන්නේ n-1 හි බිටු දකුණු කෙළවරේ සිට n දක්වා හරවා යැවීමයි. n = 6 එනම් 110 නම් 5 යනු 101 වේ නම් බිටු දකුණට සකසන ලද බිට් n සිට ප්‍රතිලෝම වේ. එබැවින් අපි සහ මේ දෙක නම් අපි සෑම පුනරාවර්තනයකදීම නිවැරදිම බිට් 0 කර සෑම විටම ඊළඟ දකුණු කෙළවරේ බිට් වෙතට යමු. හෙන්ස්, සෙට් බිට් ගණන් කිරීම. සෑම බිට් එකක්ම සකසන විට නරකම කාල සංකීර්ණතාව ඕ (ලොග්) වනු ඇත.


5

කට්ටල බිටු ගණනය කිරීම සඳහා බොහෝ ඇල්ගොරිතම ඇත; නමුත් මම හිතන්නේ හොඳම එක වේගවත් එකක්! මෙම පිටුවේ සවිස්තරාත්මකව ඔබට දැක ගත හැකිය:

බිට් ට්විඩ්ලිං හැක්ස්

මම මෙය යෝජනා කරමි:

බිටු 64, උපදෙස් භාවිතා කරමින් බිට් 14, 24, හෝ 32-බිට් වචන වලින් ගණන් කර ඇත

unsigned int v; // count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v

// option 1, for at most 14-bit values in v:
c = (v * 0x200040008001ULL & 0x111111111111111ULL) % 0xf;

// option 2, for at most 24-bit values in v:
c =  ((v & 0xfff) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;
c += (((v & 0xfff000) >> 12) * 0x1001001001001ULL & 0x84210842108421ULL) 
     % 0x1f;

// option 3, for at most 32-bit values in v:
c =  ((v & 0xfff) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;
c += (((v & 0xfff000) >> 12) * 0x1001001001001ULL & 0x84210842108421ULL) % 
     0x1f;
c += ((v >> 24) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;

මෙම ක්‍රමයට කාර්යක්ෂම වීමට වේගවත් මොඩියුලස් බෙදීමක් සහිත 64-බිට් CPU අවශ්‍ය වේ. පළමු විකල්පය අවශ්‍ය වන්නේ මෙහෙයුම් 3 ක් පමණි; දෙවන විකල්පය 10 ක් ගනී; තෙවන විකල්පය 15 ක් ගනී.


5

ආදාන ප්‍රමාණයෙන් අතු බෙදීම් සමඟ බයිට් බිට් ගණනය කිරීම් කලින් ගණනය කළ වගුව භාවිතා කරමින් වේගවත් සී # විසඳුම.

public static class BitCount
{
    public static uint GetSetBitsCount(uint n)
    {
        var counts = BYTE_BIT_COUNTS;
        return n <= 0xff ? counts[n]
             : n <= 0xffff ? counts[n & 0xff] + counts[n >> 8]
             : n <= 0xffffff ? counts[n & 0xff] + counts[(n >> 8) & 0xff] + counts[(n >> 16) & 0xff]
             : counts[n & 0xff] + counts[(n >> 8) & 0xff] + counts[(n >> 16) & 0xff] + counts[(n >> 24) & 0xff];
    }

    public static readonly uint[] BYTE_BIT_COUNTS = 
    {
        0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
    };
}

හාස්‍යයට කරුණක් නම්, මෙම ත්‍රෙඩ් එකේ පළ කර ඇති ඕනෑම ඇල්ගොරිතමයක් මඟින් එම වගුව නිර්මාණය කළ හැකිව තිබීමයි! එසේ වුවද, මේ වගේ වගු භාවිතා කිරීම යනු නියත-කාලීන ක්‍රියාකාරිත්වයයි. තවත් එක් පියවරක් ඉදිරියට ගොස් 64K පරිවර්තන වගුවක් නිර්මාණය කිරීමෙන් අවශ්‍ය AND, SHIFT සහ ADD මෙහෙයුම් අඩකින් අඩුවනු ඇත. බිට් හසුරුවන්නන් සඳහා සිත්ගන්නා මාතෘකාවක්!
user924272

හැඹිලි ගැටළු හේතුවෙන් විශාල වගු මන්දගාමී විය හැකිය (නියත-කාලය නොවේ). (0xe994 >>(k*2))&3මතක ප්‍රවේශයකින් තොරව ඔබට වරකට බිටු 3 ක් 'බැලීමට' හැකිය ...
ග්‍රෙගෝ

5

ඕනෑම ගෘහ නිර්මාණ ශිල්පයක් පිළිබඳ ඔබේ එක් එක් ඇල්ගොරිතම මිණුම් සලකුණු කළ හැකි අතේ ගෙන යා හැකි මොඩියුලයක් (ANSI-C) මෙන්න.

ඔබේ CPU එකට බිට් බයිට් 9 ක් තිබේද? ගැටළුවක් නැත :-) මේ මොහොතේ එය ඇල්ගොරිතම 2 ක්, කේ ඇන්ඩ් ආර් ඇල්ගොරිතම සහ බයිට් නැණවත් බැලීමේ වගුවක් ක්‍රියාත්මක කරයි. බැලීමේ වගුව සාමාන්‍යයෙන් K&R ඇල්ගොරිතමයට වඩා 3 ගුණයක් වේගවත් ය. "හැකර්ස් ඩයිලයිට්" ඇල්ගොරිතම අතේ ගෙන යා හැකි ක්‍රමයක් බවට පත් කිරීමට යමෙකුට හැකි නම් එය ඇතුලත් කිරීමට නිදහස්ව සිටින්න.

#ifndef _BITCOUNT_H_
#define _BITCOUNT_H_

/* Return the Hamming Wieght of val, i.e. the number of 'on' bits. */
int bitcount( unsigned int );

/* List of available bitcount algorithms.  
 * onTheFly:    Calculate the bitcount on demand.
 *
 * lookupTalbe: Uses a small lookup table to determine the bitcount.  This
 * method is on average 3 times as fast as onTheFly, but incurs a small
 * upfront cost to initialize the lookup table on the first call.
 *
 * strategyCount is just a placeholder. 
 */
enum strategy { onTheFly, lookupTable, strategyCount };

/* String represenations of the algorithm names */
extern const char *strategyNames[];

/* Choose which bitcount algorithm to use. */
void setStrategy( enum strategy );

#endif

.

#include <limits.h>

#include "bitcount.h"

/* The number of entries needed in the table is equal to the number of unique
 * values a char can represent which is always UCHAR_MAX + 1*/
static unsigned char _bitCountTable[UCHAR_MAX + 1];
static unsigned int _lookupTableInitialized = 0;

static int _defaultBitCount( unsigned int val ) {
    int count;

    /* Starting with:
     * 1100 - 1 == 1011,  1100 & 1011 == 1000
     * 1000 - 1 == 0111,  1000 & 0111 == 0000
     */
    for ( count = 0; val; ++count )
        val &= val - 1;

    return count;
}

/* Looks up each byte of the integer in a lookup table.
 *
 * The first time the function is called it initializes the lookup table.
 */
static int _tableBitCount( unsigned int val ) {
    int bCount = 0;

    if ( !_lookupTableInitialized ) {
        unsigned int i;
        for ( i = 0; i != UCHAR_MAX + 1; ++i )
            _bitCountTable[i] =
                ( unsigned char )_defaultBitCount( i );

        _lookupTableInitialized = 1;
    }

    for ( ; val; val >>= CHAR_BIT )
        bCount += _bitCountTable[val & UCHAR_MAX];

    return bCount;
}

static int ( *_bitcount ) ( unsigned int ) = _defaultBitCount;

const char *strategyNames[] = { "onTheFly", "lookupTable" };

void setStrategy( enum strategy s ) {
    switch ( s ) {
    case onTheFly:
        _bitcount = _defaultBitCount;
        break;
    case lookupTable:
        _bitcount = _tableBitCount;
        break;
    case strategyCount:
        break;
    }
}

/* Just a forwarding function which will call whichever version of the
 * algorithm has been selected by the client 
 */
int bitcount( unsigned int val ) {
    return _bitcount( val );
}

#ifdef _BITCOUNT_EXE_

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* Use the same sequence of pseudo random numbers to benmark each Hamming
 * Weight algorithm.
 */
void benchmark( int reps ) {
    clock_t start, stop;
    int i, j;
    static const int iterations = 1000000;

    for ( j = 0; j != strategyCount; ++j ) {
        setStrategy( j );

        srand( 257 );

        start = clock(  );

        for ( i = 0; i != reps * iterations; ++i )
            bitcount( rand(  ) );

        stop = clock(  );

        printf
            ( "\n\t%d psudoe-random integers using %s: %f seconds\n\n",
              reps * iterations, strategyNames[j],
              ( double )( stop - start ) / CLOCKS_PER_SEC );
    }
}

int main( void ) {
    int option;

    while ( 1 ) {
        printf( "Menu Options\n"
            "\t1.\tPrint the Hamming Weight of an Integer\n"
            "\t2.\tBenchmark Hamming Weight implementations\n"
            "\t3.\tExit ( or cntl-d )\n\n\t" );

        if ( scanf( "%d", &option ) == EOF )
            break;

        switch ( option ) {
        case 1:
            printf( "Please enter the integer: " );
            if ( scanf( "%d", &option ) != EOF )
                printf
                    ( "The Hamming Weight of %d ( 0x%X ) is %d\n\n",
                      option, option, bitcount( option ) );
            break;
        case 2:
            printf
                ( "Please select number of reps ( in millions ): " );
            if ( scanf( "%d", &option ) != EOF )
                benchmark( option );
            break;
        case 3:
            goto EXIT;
            break;
        default:
            printf( "Invalid option\n" );
        }

    }

 EXIT:
    printf( "\n" );

    return 0;
}

#endif

1
ඔබගේ ප්ලග්-ඉන්, බහුමාමක ප්‍රවේශයට මෙන්ම නැවත භාවිතා කළ හැකි පුස්තකාලයක් ලෙස හෝ තනිවම, පරීක්‍ෂා කළ හැකි ලෙස ගොඩනගා ගැනීමට මම කැමතියි. ඉතා හොඳින් සිතුවා =)
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.