B নম্বর প্রতিনিধিত্বকারী 8 বিটগুলি এর মতো দেখতে:
00000111
তিন বিট সেট করা হয়।
32-বিট পূর্ণসংখ্যায় সেট বিটের সংখ্যা নির্ধারণের জন্য অ্যালগরিদম কী কী?
B নম্বর প্রতিনিধিত্বকারী 8 বিটগুলি এর মতো দেখতে:
00000111
তিন বিট সেট করা হয়।
32-বিট পূর্ণসংখ্যায় সেট বিটের সংখ্যা নির্ধারণের জন্য অ্যালগরিদম কী কী?
উত্তর:
এটি ' হামিং ওজন ', 'পপকাউন্ট' বা 'পাশাপাশি যুক্ত হওয়া' নামে পরিচিত ।
'সেরা' অ্যালগরিদম নির্ভর করে আপনি কোন সিপিইউতে আছেন এবং আপনার ব্যবহারের ধরণটি কী তার উপর নির্ভর করে।
কিছু সিপিইউতে এটি করার জন্য একটি একক অন্তর্নিহিত নির্দেশনা রয়েছে এবং অন্যদের মধ্যে সমান্তরাল নির্দেশ রয়েছে যা বিট ভেক্টরগুলিতে কাজ করে। সমান্তরাল নির্দেশাবলী (x86 এর মতো popcnt
, সিপিইউগুলিতে যেখানে এটি সমর্থিত) প্রায় দ্রুততম হবে। কিছু অন্যান্য আর্কিটেকচারের একটি মাইক্রোকোডযুক্ত লুপের সাহায্যে ধীর নির্দেশ প্রয়োগ করা যেতে পারে যা চক্র প্রতি কিছুটা পরীক্ষা করে থাকে ( উদ্ধৃতি আবশ্যক )।
প্রাক-জনবহুল সারণী দেখার পদ্ধতিটি যদি আপনার সিপিইউতে একটি বড় ক্যাশে থাকে এবং / অথবা আপনি একটি দৃ tight় লুপে এই নির্দেশাবলী প্রচুর পরিমাণে করেন তবে খুব দ্রুত হতে পারে। তবে এটি 'ক্যাশে মিস' ব্যয়ের কারণে ক্ষতিগ্রস্থ হতে পারে, যেখানে সিপিইউকে মূল স্মৃতি থেকে কিছু টেবিল আনতে হবে। (টেবিলটি ছোট রাখতে প্রতিটি বাইট আলাদাভাবে দেখুন))
যদি আপনি জানেন যে আপনার বাইটগুলি বেশিরভাগ 0 এর বা বেশিরভাগ 1 এর হবে তবে এই পরিস্থিতিগুলির জন্য খুব দক্ষ অ্যালগরিদম রয়েছে are
আমি বিশ্বাস করি একটি খুব ভাল সাধারণ উদ্দেশ্য অ্যালগরিদম নিম্নলিখিত, 'সমান্তরাল' বা 'ভেরিয়েবল-নির্ভুলতা 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);
এটি আলোচিত যে কোনও অ্যালগরিদমের সবচেয়ে খারাপ-আচরণের আচরণ করে, তাই আপনি যে কোনও ব্যবহারের প্যাটার্ন বা মান এতে নিক্ষিপ্ত করেন তা দক্ষতার সাথে মোকাবেলা করবে।
i = i - ((i >> 1) & 0x55555555);
প্রথম পদক্ষেপটি বিজোড় / এমনকি বিটগুলি বিচ্ছিন্ন করার জন্য তাদের মাস্কিংয়ের অপ্টিমাইজড সংস্করণ, সেগুলি সরিয়ে রাখার জন্য স্থানান্তর করা এবং যুক্ত করা। এটি কার্যকরভাবে 2-বিট সংগ্রহকারীগুলিতে 16 টি পৃথক সংযোজন করে ( একটি রেজিস্ট্রারের মধ্যে SWAR = সিমড )। লাইক (i & 0x55555555) + ((i>>1) & 0x55555555)
।
পরবর্তী পদক্ষেপটি 16x 2-বিট সংগ্রহকারীগুলির মধ্যে বিজোড় / এমনকি আটটি নেয় এবং 8x 4-বিট পরিমাণ যোগ করে আবার যুক্ত করে। i - ...
তাই এটি ঠিক আগে মাস্ক নেই / নাড়াচাড়া পর অপ্টিমাইজেশান এই সময় সম্ভব নয়। স্থান পরিবর্তন করার আগে পরিবর্তনের 0x33...
পরিবর্তে দু'বার একই ধ্রুবক ব্যবহার 0xccc...
করা একটি ভাল জিনিস যখন আইএসএগুলির জন্য পৃথকভাবে 32-বিট ধ্রুবক নির্মাণ করা প্রয়োজন সংকলন করার সময়।
চূড়ান্ত শিফট এবং যুক্ত পদক্ষেপটি (i + (i >> 4)) & 0x0F0F0F0F
4x 8-বিট আহরণকারীগুলিতে প্রশস্ত হয়। এটি পূর্বের পরিবর্তে যুক্ত করার পরে মুখোশ পরে , কারণ যে কোনও 4-বিট সঞ্চালকের সর্বাধিক মান 4
যদি সংশ্লিষ্ট ইনপুট বিটের সমস্ত 4 বিট সেট করা থাকে। 4 + 4 = 8 যা এখনও 4 টি বিটের সাথে খাপ খায় তাই নিবল উপাদানগুলির মধ্যে বহন করা অসম্ভব i + (i >> 4)
।
এখনও অবধি এটি কয়েকটি চালাক অপ্টিমাইজেশনের সাথে স্বর কৌশল ব্যবহার করে মোটামুটি স্বাভাবিক সিমড। আরও 2 টি ধাপের জন্য একই প্যাটার্নটির সাথে চালিয়ে যাওয়া 2x 16-বিট এর পরে 1x 32-বিট গুনে প্রশস্ত হতে পারে। তবে দ্রুত হার্ডওয়্যার গুণিত সহ মেশিনগুলিতে আরও কার্যকর উপায় রয়েছে:
আমাদের একবার পর্যাপ্ত পরিমাণে "উপাদান" হয়ে গেলে, ম্যাজিক ধ্রুবক সহ একটি গুণটি সমস্ত উপাদানকে শীর্ষ উপাদানগুলিতে যোগ করতে পারে । এই ক্ষেত্রে বাইট উপাদান। বাম-স্থানান্তর এবং যোগ দ্বারা গুণ করা হয়, x * 0x01010101
ফলস্বরূপ একটি গুণ গুণ x + (x<<8) + (x<<16) + (x<<24)
। আমাদের 8-বিট উপাদান যথেষ্ট চওড়া (এবং ছোট যথেষ্ট গন্য অধিষ্ঠিত) যে এই বহন উত্পাদন না হয় মধ্যে উপরের 8 বিট।
এর একটি 64-বিট সংস্করণ 0x010101010101010101 গুণক সহ একটি 64-বিট পূর্ণসংখ্যায় 8x 8-বিট উপাদান করতে পারে এবং এর সাথে উচ্চ বাইটটি বের করতে পারে >>56
। সুতরাং এটি কোনও অতিরিক্ত পদক্ষেপ নেয় না, কেবল বৃহত্তর ধ্রুবক। __builtin_popcountll
যখন হার্ডওয়্যার popcnt
নির্দেশনা সক্ষম করা না হয় তখন x86 সিস্টেমে এটি জিসিসি ব্যবহার করে । আপনি যদি এর জন্য বিল্টিনস বা ইন্টারসিনিকগুলি ব্যবহার করতে পারেন তবে কম্পাইলারকে লক্ষ্য-নির্দিষ্ট অপ্টিমাইজেশান করার সুযোগ দেওয়ার জন্য এটি করুন।
এই বিটওয়াইস-এসওয়ার অ্যালগরিদমটি সিমড সহ সিপিইউগুলিতে দ্রুতগতির জন্য একক পূর্ণসংখ্যার নিবন্ধের পরিবর্তে একাধিক ভেক্টর উপাদানগুলিতে একবারে সম্পন্ন করার জন্য সমান্তরাল হতে পারে তবে কোনও ব্যবহারযোগ্য পপকাউন্ট নির্দেশনা নেই। (যেমন x86-64 কোড যা কেবলমাত্র নেহালেম বা তার পরে নয়, কোনও সিপিইউতে চালাতে হবে later)
তবে পপকাউন্টের জন্য ভেক্টর নির্দেশাবলীর ব্যবহারের সর্বোত্তম উপায় হ'ল সমান্তরালে প্রতিটি বাইটের এক সাথে 4 টি বিটের জন্য একটি টেবিল-সন্ধানের জন্য ভেরিয়েবল-শ্যাফেল ব্যবহার করে। (4 বিটস সূচক ভেক্টর রেজিস্টারে রাখা 16 টি এন্ট্রি টেবিল)।
ইন্টেল সিপিইউগুলিতে, হার্ডওয়্যার bit৪ বিট পপসেন্ট নির্দেশাবলী একটি এসএসএসই 3 PSHUFB
বিট-সমান্তরাল বাস্তবায়নকে প্রায় 2 এর একটি ফ্যাক্টর দ্বারা কার্যকর করতে পারে , তবে কেবলমাত্র যদি আপনার সংকলকটি ঠিক এটি পায় । অন্যথায় এসএসই উল্লেখযোগ্যভাবে সামনে আসতে পারে। আরও নতুন সংকলক সংস্করণগুলি ইন্টেলের পপসেন্ট মিথ্যা নির্ভরতা সমস্যা সম্পর্কে সচেতন ।
তথ্যসূত্র:
unsigned int
সহজেই এটি ব্যবহার করা উচিত , এটি সহজেই দেখানোর জন্য যে এটি কোনও সাইন বিট জটিলতা থেকে মুক্ত। এছাড়াও uint32_t
নিরাপদ হবে, যেমনটি, আপনি সমস্ত প্ল্যাটফর্মগুলিতে যা প্রত্যাশা করেন তা পাবেন?
>>
নেতিবাচক মান জন্য বাস্তবায়ন সংজ্ঞায়িত হয়। যুক্তিটি পরিবর্তন করতে (বা কাস্ট করা) দরকার unsigned
, এবং যেহেতু কোডটি 32-বিট-নির্দিষ্ট, সম্ভবত এটি ব্যবহার করা উচিত uint32_t
।
আপনার সংকলকগুলির অন্তর্নির্মিত ফাংশনগুলিও বিবেচনা করুন।
GNU সংকলক উদাহরণস্বরূপ আপনি কেবল ব্যবহার করতে পারেন:
int __builtin_popcount (unsigned int x);
int __builtin_popcountll (unsigned long long x);
সবচেয়ে খারাপ ক্ষেত্রে সংকলক একটি ফাংশনে কল উত্পন্ন করবে। সেরা ক্ষেত্রে সংকলক একই কাজটি দ্রুত করার জন্য সিপিইউ নির্দেশিকা নির্গত করবে।
জিসিসি ইন্টারসনিক্স এমনকি একাধিক প্ল্যাটফর্ম জুড়ে কাজ করে। পপকাউন্টটি x86 আর্কিটেকচারে মূলধারায় পরিণত হবে, সুতরাং এখনই অভ্যন্তরীণ ব্যবহার শুরু করা বুদ্ধিমান হয়ে উঠবে। অন্যান্য স্থাপত্যগুলিতে বছরের পর বছর ধরে পপকাউন্ট রয়েছে।
X86-এ, আপনি সংকলকটি বলতে পারেন যে এটি একই প্রজন্মের মধ্যে যুক্ত ভেক্টর নির্দেশাবলী popcnt
সহ নির্দেশের জন্য -mpopcnt
বা -msse4.2
এটি সক্ষম করার জন্য সমর্থন গ্রহণ করতে পারে । দেখুন জিসিসি এক্স 86 অপশন । -march=nehalem
(বা -march=
আপনার কোডটি ধরে নিতে এবং এর জন্য টিউন করার জন্য আপনি যে কোনও সিপিইউ চান) ভাল পছন্দ হতে পারে। পুরানো সিপিইউতে ফলাফল বাইনারি চালানোর ফলে একটি অবৈধ-নির্দেশ ত্রুটি হবে।
আপনি যে মেশিনটি তৈরি করেন তার জন্য বাইনারিগুলি অপ্টিমাইজড করতে, -march=native
(জিসিসি, ঝনঝন বা আইসিসি সহ) ব্যবহার করুন।
এমএসভিসি x86 popcnt
নির্দেশের জন্য একটি আন্তঃনীতি সরবরাহ করে তবে জিসিসির বিপরীতে এটি হার্ডওয়্যার নির্দেশের জন্য সত্যই অন্তর্নিহিত এবং হার্ডওয়্যার সমর্থন প্রয়োজন।
std::bitset<>::count()
বিল্ট-ইন এর পরিবর্তে ব্যবহার করা হচ্ছে
তত্ত্ব অনুসারে, যে কোনও সংকলক যে কীভাবে টার্গেট সিপিইউতে দক্ষতার সাথে পপকাউন্ট করতে জানে তার কার্যকারিতাটি আইএসও সি ++ এর মাধ্যমে প্রকাশ করা উচিত std::bitset<>
। অনুশীলনে, কিছু লক্ষ্যযুক্ত সিপিইউগুলির জন্য আপনি কিছু ক্ষেত্রে বিট-হ্যাক ওআর / শিফট / এডিডি দিয়ে ভাল হতে পারেন।
টার্গেট আর্কিটেকচারের জন্য যেখানে হার্ডওয়্যার পপকাউন্ট একটি alচ্ছিক এক্সটেনশন (x86 এর মতো), সমস্ত সংকলক std::bitset
উপলব্ধ থাকে না যখন এটি সুবিধা গ্রহণ করে। উদাহরণস্বরূপ, popcnt
সংকলনের সময় এমএসভিসির কাছে সমর্থন সক্ষম করার কোনও উপায় নেই এবং সর্বদা একটি সারণী অনুসন্ধানও ব্যবহার করে , এমনকি এটি /Ox /arch:AVX
(যা এসএসই 4.2 বোঝায়, যদিও প্রযুক্তিগতভাবে আলাদা বৈশিষ্ট্যযুক্ত বিট রয়েছে is popcnt
)
তবে কমপক্ষে আপনি পোর্টেবল এমন কিছু পান যা সর্বত্র কাজ করে, এবং জিসিসি / ঝাঁকুনির সাথে সঠিক লক্ষ্য বিকল্পের সাহায্যে আপনি আর্কিটেকচারের জন্য হার্ডওয়্যার পপকাউন্ট পাবেন।
#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();
}
দেখুন জিসিসি, ঝনঝন শব্দ, আইসিসি এবং MSVC থেকে এ এস এম Godbolt কম্পাইলার এক্সপ্লোরার উপর।
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
পাওয়ারপিসি 64 প্রকাশ করে 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- নির্দিষ্ট নয়, তবে কেবল gcc / ক্ল্যাং / আইসিসি সহ x86 এর জন্য ভাল সংকলন করে।
আরও মনে রাখবেন যে একক-নির্দেশনা পপকাউন্ট ছাড়াই আর্কিটেকচারের জন্য জিসিসির ফ্যালব্যাক একটি সময়ে বাইট-এ-টাইম টেবিল লুকআপ। উদাহরণস্বরূপ এটি আর্মের পক্ষে দুর্দান্ত নয় ।
std::bitset::count
। একটি একক __builtin_popcount
কলে এই সংকলন অন্তর্ভুক্ত করার পরে ।
আমার মতে, "সেরা" সমাধানটি হ'ল এক যা অন্য প্রোগ্রামার (বা মূল প্রোগ্রামার দুই বছর পরে) প্রচুর মন্তব্য ছাড়াই পড়তে পারে। আপনি ভাল বা দ্রুততম সমাধানটি ভালভাবে চাইতে পারেন যা কিছু ইতিমধ্যে সরবরাহ করেছে তবে আমি যে কোনও সময় চতুরতার চেয়ে পাঠযোগ্যতা পছন্দ করি।
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);
}
যদিও এগুলি নির্দিষ্ট ডেটা ধরণের মাপের উপর নির্ভর করে তাই তারা যে পোর্টেবল নয়। তবে, যেহেতু অনেকগুলি পারফরম্যান্স অপটিমাইজেশন কোনওভাবেই বহনযোগ্য নয়, এটি কোনও সমস্যা নয়। আপনি যদি বহনযোগ্যতা চান তবে আমি পঠনযোগ্য সমাধানটিতে আছি।
if ((value & 1) == 1) { count++; }
দিয়ে প্রতিস্থাপন করা আরও বোধগম্য হবে না count += value & 1
?
হ্যাকার্স ডিলাইট থেকে, পি। 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;
}
Branch 20-ইশ নির্দেশাবলী (আর্চ নির্ভর) কার্যকর করে, কোনও শাখা ছাড়াই।
হ্যাকার কল্লোল হয় আনন্দদায়ক! অত্যন্ত বাঞ্ছনীয়.
Integer.bitCount(int)
একই একই বাস্তবায়ন ব্যবহার করা হয়।
pop
পরিবর্তে population_count
(বা pop_cnt
যদি আপনার কোনও অবসন্নতা থাকতেই পারে) এর পরিবর্তে যে কাউকে ফোন দিলে আমি একটি ভাল লাথি দেব । @ মারকোবোলিস আমি অনুমান করি যে এটি জাভার সমস্ত সংস্করণের ক্ষেত্রে সত্য হবে, তবে আনুষ্ঠানিকভাবে এটি বাস্তবায়নের উপর নির্ভরশীল হবে :)
আমি মনে করি দ্রুততম উপায়ে - অনুসন্ধানের সারণী এবং পপকাউন্ট ব্যবহার না করে — নীচের এটি। এটি মাত্র 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
তারপরে আমরা উপরের ফলাফলটি সংযুক্ত করি, আমাদেরকে 4 বিটে সেট বিটের মোট গণনা প্রদান করে। সর্বশেষ বিবৃতিটি সবচেয়ে জটিল।
c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
আসুন এটি আরও ভেঙে দিন ...
v + (v >> 4)
এটি দ্বিতীয় বিবৃতি অনুরূপ; পরিবর্তে আমরা 4 টি গ্রুপে সেট বিটগুলি গণনা করছি। আমরা জানি our আমাদের পূর্ববর্তী ক্রিয়াকলাপগুলির কারণে n যে প্রতিটি স্তন্যপায়ী এতে সেট বিটের সংখ্যা রয়েছে। একটি উদাহরণ তাকান। ধরুন আমাদের বাইট আছে 0b01000010
। এর অর্থ হ'ল প্রথম স্তন্যপানটির 4 বিট সেট রয়েছে এবং দ্বিতীয়টির 2 বিট সেট রয়েছে। এখন আমরা এই নিবলগুলি একসাথে যুক্ত করব।
0b01000010 + 0b01000000
এটি আমাদের প্রথম বিড়ালের মধ্যে একটি বাইটে সেট বিটের গণনা দেয় 0b01100010
এবং অতএব আমরা সংখ্যার সমস্ত বাইটের শেষ চারটি বাইটকে মুখোশ করি (সেগুলি ত্যাগ করে)।
0b01100010 & 0xF0 = 0b01100000
এখন প্রতিটি বাইটে এতে সেট বিটের গণনা রয়েছে। আমাদের এগুলি সমস্ত একসাথে যুক্ত করতে হবে। কৌশলটি হ'ল ফলাফলটির গুণন করা 0b10101010
যার দ্বারা একটি আকর্ষণীয় সম্পত্তি রয়েছে। যদি আমাদের সংখ্যার চারটি বাইট থাকে, A B C D
তবে এই বাইটগুলির সাথে এটি একটি নতুন সংখ্যার ফলাফল হবেA+B+C+D B+C+D C+D D
। একটি 4 বাইট সংখ্যায় সর্বাধিক 32 বিট সেট থাকতে পারে, যা হিসাবে উপস্থাপিত হতে পারে 0b00100000
।
আমাদের এখন যা দরকার তা হ'ল প্রথম বাইট যা সমস্ত বাইটে সমস্ত সেট বিটের সমষ্টি করে এবং আমরা এটি পাই >> 24
। এই অ্যালগরিদম 32 bit
শব্দের জন্য ডিজাইন করা হয়েছিল তবে শব্দের জন্য সহজেই পরিবর্তন করা যেতে পারে 64 bit
।
c =
সম্পর্কে? দেখে মনে হচ্ছে বাদ দেওয়া উচিত। আরও কিছু ক্লাসিক সতর্কতা এড়াতে অতিরিক্ত "পেরেন" এ (((ভি + (ভি >> 4)) এবং 0xF0F0F0F) * 0x1010101) >> 24 "প্রস্তাব দিন।
popcount(int v)
এবং popcount(unsigned v)
। বহনযোগ্যতার জন্য, বিবেচনা করুন popcount(uint32_t v)
, ইত্যাদি সত্যিই * 0x1010101 অংশটি পছন্দ করুন।
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
সুতরাং আপনি আসলে কী করছেন তা দেখার জন্য আমাদের চিঠিগুলি গণনা করার দরকার নেই (যেহেতু আপনি প্রথমটি বাতিল করেছেন 0
, আমি ভুলক্রমে ভেবেছিলাম যে আপনি ভুল (উল্টানো) বিট প্যাটার্নটি মাস্ক হিসাবে ব্যবহার করেছেন - এটি উল্লেখ না করা পর্যন্ত কেবলমাত্র 7 টি বর্ণ রয়েছে এবং 8 টি নয়)।
আমি বিরক্ত হয়েছি, এবং তিনটি পদ্ধতির এক বিলিয়ন পুনরাবৃত্তি করেছি। সংকলকটি জিসিসি -ও 3। সিপিইউ হ'ল যা তারা 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 সেকেন্ড সময় নেয়।
চতুর্থ স্থানটি জিসিসির __ বিল্টিন_পপ্যাক্ট () -এ যায়, লজ্জাজনকভাবে 11 সেকেন্ডে।
এক-সময়ে-সময়ে গণনাটি ওয়াআআএএই ধীর ছিল এবং এটি শেষ হওয়ার অপেক্ষায় আমি বিরক্ত হয়ে পড়েছি।
সুতরাং আপনি যদি সর্বোপরি পারফরম্যান্সের বিষয়ে যত্নশীল হন তবে প্রথম পদ্ধতির ব্যবহার করুন। আপনি যদি যত্নশীল হন তবে এটিতে K৪ কেবি র্যাম ব্যয় করার পক্ষে পর্যাপ্ত পরিমাণ নেই, দ্বিতীয় পদ্ধতির ব্যবহার করুন। অন্যথায় পাঠযোগ্য (তবে ধীর) এক-বিট-এ-এ-সময় পদ্ধতির ব্যবহার করুন।
এমন পরিস্থিতি সম্পর্কে ভাবতে অসুবিধা হয় যেখানে আপনি বিট-টুইডলিং পদ্ধতির ব্যবহার করতে চান।
সম্পাদনা করুন: এখানে অনুরূপ ফলাফল ।
আপনি যদি জাভা ব্যবহার করে যাচ্ছেন, অন্তর্নির্মিত পদ্ধতিটি এটি Integer.bitCount
করবে।
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;
}
আমাকে এই অ্যালগরিদম ব্যাখ্যা করুন।
এই অ্যালগরিদম বিভাজন এবং বিজয়ী অ্যালগোরিদমের উপর ভিত্তি করে। ধরুন এখানে 8 বিট পূর্ণসংখ্যা 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)
+-------------------------------+
এটি সেই প্রশ্নগুলির মধ্যে একটি যেখানে এটি আপনার মাইক্রো-আর্কিটেকচারটি জানতে সহায়তা করে। আমি সেক্ষেত্রে জিসিসি ৪.৩.৩ এর অধীনে দুটি বৈকল্পিক টাইম করেছি - ফাংশন কল ওভারহেড নির্মূল করতে সি ++ ইনলাইন ব্যবহার করে এক বিলিয়ন পুনরাবৃত্তি, টাইপিংয়ের জন্য আরডিএসসি ব্যবহার করে গুরুত্বপূর্ণ কিছু মুছে ফেলছে না তা নিশ্চিত করার জন্য সমস্ত গুনের চলমান যোগফল রেখেছি ( ঘড়ি চক্র সুনির্দিষ্ট)।
ইনলাইন ইন পপ 2 (স্বাক্ষরযুক্ত এক্স, স্বাক্ষরযুক্ত 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 গিগ্যাসিকেল নিয়েছিল। আমার সমান্তরাল সংস্করণ (বহু বিটের দ্বিগুণ গণনা) ১৩.০ গিগ্যাসিচলে চলে। 10.5s মোট ২.৪ গিগাহার্টজ কোর ডুওয়ে দুজনের জন্যই কেটে গেছে। এই ঘড়ির ফ্রিকোয়েন্সিতে 25 গিগ্যাসিলেস = মাত্র 10 সেকেন্ডের বেশি, তাই আমি নিশ্চিত যে আমার সময় সঠিক।
এটি নির্দেশ নির্ভরতা শৃঙ্খলাগুলির সাথে করতে হবে যা এই অ্যালগরিদমের পক্ষে খুব খারাপ। আমি এক জোড়া 64৪-বিট রেজিস্টার ব্যবহার করে আবার গতি প্রায় দ্বিগুণ করতে পারি। প্রকৃতপক্ষে, আমি যদি চালাক এবং এক্স + ইয়া যুক্ত করি তবে খুব শীঘ্রই আমি কিছু শিফট শেভ করতে পারতাম। কিছু ছোট টুইটের সাথে -৪-বিট সংস্করণটি প্রায় প্রকাশিত হবে তবে আবার দ্বিগুণ হিসাবে বিটগুলি গণনা করুন।
128 বিট সিমডি রেজিস্টার সহ, দুটি আরও একটি কারণ, এবং এসএসই নির্দেশিকা সেটগুলিতে প্রায়শই চতুর শর্ট-কাট রয়েছে।
কোডটি বিশেষভাবে স্বচ্ছ হওয়ার কোনও কারণ নেই। ইন্টারফেসটি সহজ, অ্যালগরিদমটি অনেক জায়গায় অন-লাইনে উল্লেখ করা যেতে পারে এবং এটি ইউনিট বিস্তৃত পরীক্ষার জন্য উপযুক্ত। প্রোগ্রামার যিনি এতে হোঁচট খায় সে এমনকি কিছু শিখতে পারে। এই বিট অপারেশনগুলি মেশিন পর্যায়ে অত্যন্ত প্রাকৃতিক।
ঠিক আছে, আমি টুইট করা -৪-বিট সংস্করণটি বেঞ্চ করার সিদ্ধান্ত নিয়েছি। এটির জন্য এক আকারের (স্বাক্ষরযুক্ত দীর্ঘ) == 8
ইনলাইন ইন পপ 2 (স্বাক্ষরবিহীন দীর্ঘ এক্স, স্বাক্ষরযুক্ত দীর্ঘ y) { x = x - ((x >> 1) & 0x5555555555555555); y = y - ((y >> 1) & 0x5555555555555555); x = (x এবং 0x333333333333333333) + ((x >> 2) & 0x333333333333333333); y = (y & 0x333333333333333333) + ((y >> 2) & 0x333333333333333333); x = (x + (x >> 4)) এবং 0x0F0F0F0F0F0F0F0F0F; y = (y + (y >> 4)) এবং 0x0F0F0F0F0F0F0F0F0F; x = x + y; x = x + (x >> 8); x = x + (x >> 16); x = x + (x >> 32); এক্স & 0xFF ফিরে; }
এটি সঠিক সম্পর্কে দেখায় (যদিও আমি সাবধানে পরীক্ষা করছি না)। এখন সময়গুলি 10.70 গিগ্যাসিলে / 14.1 গিগ্যাসিলে বের হয়। পরে এটির সংখ্যা 128 বিলিয়ন বিট এবং এই মেশিনে বিগত 5.9 এর সাথে মিলে যায়। অ সমান্তরাল সংস্করণটি একটি সামান্য বিট বাড়ায় কারণ আমি 64৪-বিট মোডে চলছি এবং এটি 32৪-বিট রেজিস্টারের চেয়ে 64৪-বিট রেজিস্টারগুলিকে পছন্দ করে।
আসুন দেখুন এখানে আরও কিছু OO পাইপলাইনিং রয়েছে কিনা। এটি কিছুটা বেশি জড়িত ছিল, তাই আমি আসলে কিছুটা পরীক্ষা করেছি। প্রতিটি পদ এককভাবে 64৪ এর সমষ্টি, সমস্ত মিলিত পরিমাণ 256।
ইনলাইন ইন পপ 4 (স্বাক্ষরবিহীন দীর্ঘ এক্স, স্বাক্ষরযুক্ত লম্বা y, স্বাক্ষরবিহীন দীর্ঘ ইউ, স্বাক্ষরযুক্ত দীর্ঘ ভি) { এনাম {এম 1 = 0x5555555555555555, এম 2 = 0x3333333333333333, এম 3 = 0x0F0F0F0F0F0F0F0F, m4 = 0x000000FF000000FF}; x = x - ((x >> 1) & এম 1); y = y - ((y >> 1) & এম 1); u = u - ((u >> 1) & এম 1); v = v - ((v >> 1) & এম 1); x = (x & m2) + ((x >> 2) & এম 2); y = (y & m2) + ((y >> 2) & এম 2); u = (u & m2) + ((u >> 2) & এম 2); v = (v & m2) + ((v >> 2) & এম 2); x = x + y; u = u + v; x = (x & m3) + ((x >> 4) & এম 3); u = (u & m3) + ((u >> 4) & এম 3); x = x + u; x = x + (x >> 8); x = x + (x >> 16); x = এক্স & এম 4; x = x + (x >> 32); এক্স & 0x000001FF রিটার্ন করুন; }
আমি এক মুহুর্তের জন্য উচ্ছ্বসিত ছিলাম, তবে দেখা যাচ্ছে যে আমি কিছু পরীক্ষায় ইনলাইন কীওয়ার্ডটি ব্যবহার করছি না, যদিও জিসিসি -O3 এর সাথে ইনলাইন কৌশলগুলি খেলছে। আমি যখন জিসিসি কৌশল খেলি, পপ 4 এ এক বিলিয়ন কল () 12.56 গিগ্যাসিকেল নেয়, কিন্তু আমি স্থির করেছিলাম যে এটি আর্গুমেন্টগুলি স্থির মত প্রকাশ হিসাবে ভাঁজ করা হয়েছিল। আরও 30% স্পিড-আপের জন্য আরও বাস্তবসম্মত সংখ্যা 19.6gc বলে মনে হয়। আমার পরীক্ষার লুপটি এখন দেখতে এরকম, এটি নিশ্চিত করে ট্র্যাক খেলতে জিসিসি থামাতে প্রতিটি যুক্তি যথেষ্ট আলাদা।
হিটাইম বি 4 = আরডিটিএসসি (); (স্বাক্ষরযুক্ত দীর্ঘ i = 10L * 1000 * 1000 * 1000; i <11L * 1000 * 1000 * 1000; ++ আমি) যোগ + = পপ 4 (আমি, আমি ^ 1, ~ i, i | 1); হিটাইম e4 = rdtsc ();
8.17 সেকেন্ডে 256 বিলিয়ন বিট যোগ হয়েছে। 16-বিট সারণী অনুসন্ধানে বেঞ্চমার্ক হিসাবে 32 মিলিয়ন বিটের জন্য 1.02s এ কাজ করে। সরাসরি তুলনা করতে পারছি না, কারণ অন্য বেঞ্চটি একটি ঘড়ির গতি দেয় না, তবে দেখে মনে হচ্ছে যে আমি 64 কেবি টেবিল সংস্করণে সটকে বাইরে বের করে দিয়েছি, এটি প্রথম স্থানে এল 1 ক্যাশের করুণ ব্যবহার।
আপডেট: আরও চারটি নকল লাইন যুক্ত করে সুস্পষ্ট করার এবং পপ 6 () তৈরি করার সিদ্ধান্ত নিয়েছে। 22.8gc এ এসেছিল, 9.5 সেকেন্ডে 384 বিলিয়ন বিট যোগ হয়েছে। সুতরাং 32 মিলিয়ন বিটের জন্য 800ms এ এখন আরও 20% রয়েছে।
কেন পুনরাবৃত্তভাবে 2 দ্বারা বিভক্ত হয় না?
গণনা = 0 যখন এন> 0 যদি (n% 2) == 1 গণনা + = 1 এন / = 2
আমি সম্মত হই যে এটি দ্রুততম নয়, তবে "সেরা" কিছুটা অস্পষ্ট। আমি যুক্তি দিচ্ছি যদিও "সেরা" এর স্পষ্টতার একটি উপাদান থাকা উচিত
আপনি বিট নিদর্শনগুলি লিখলে হ্যাকারের আনন্দের বিট-টুইডলিং এত বেশি স্পষ্ট হয়ে ওঠে becomes
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;
}
প্রথম পদক্ষেপটি বিজোড় বিটগুলিতে এমনকি বিটগুলি যোগ করে, প্রতিটি দুটিতে বিট যোগ করে। অন্য পদক্ষেপগুলি কম-অর্ডার খণ্ডগুলিতে উচ্চ-ক্রমযুক্ত অংশগুলি যুক্ত করে, পুরো আকারটি দ্বিগুণ করে, যতক্ষণ না আমাদের কাছে শেষ অবধি শেষ অবধি শেষ হয়।
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;
}
এটি করা যেতে পারে O(k)
, যেখানে k
বিটের সংখ্যা সেট রয়েছে।
int NumberOfSetBits(int n)
{
int count = 0;
while (n){
++ count;
n = (n - 1) & n;
}
return count;
}
n &= (n-1)
রূপটি ব্যবহার করেছিলেন ।
এটি দ্রুত বা সর্বোত্তম সমাধান নয়, তবে আমি আমার পথে একই প্রশ্নটি পেয়েছি এবং আমি ভাবতে এবং ভাবতে শুরু করি। অবশেষে আমি বুঝতে পেরেছিলাম যে এটি গাণিতিক দিক থেকে সমস্যাটি পেয়ে যদি এটি এমনভাবে করা যায় এবং একটি গ্রাফ আঁকেন, তবে আপনি দেখতে পাবেন এটি একটি ফাংশন যার কিছু পর্যায়ক্রমিক অংশ রয়েছে, এবং তারপরে আপনি পিরিয়ডের মধ্যে পার্থক্য বুঝতে পারবেন ... তাই আপনি এখানে যান:
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);
}
}
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))()
আপনি যে ফাংশনটির জন্য সন্ধান করছেন তা প্রায়ই বাইনারি সংখ্যার "পাশের সমষ্টি" বা "জনসংখ্যা গণনা" নামে পরিচিত। নথ এটিকে প্রাক-ফ্যাসিক্যাল 1 এ, পিপি 11-12-তে আলোচনা করেছেন (যদিও খণ্ড 2, 4.6.3- (7) এ সংক্ষিপ্ত রেফারেন্স ছিল))
রুম classicus পিটার Wegner এর নিবন্ধ "একটি বাইনারি কম্পিউটার কাউন্টিং ব্যক্তিদের একটি কৌশল", থেকে এসিএম এর কমিউনিকেশনস , ভলিউম 3 (1960) নম্বর 5, পৃষ্ঠা 322 । তিনি সেখানে দুটি পৃথক পৃথক অ্যালগরিদম দিয়েছেন, একটি "স্পারস" বলে প্রত্যাশিত সংখ্যার জন্য অপ্টিমাইজড (অর্থাত, একটি সংখ্যক সংখ্যক রয়েছে) এবং একটি বিপরীত মামলার জন্য।
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;
}
কয়েকটি মুক্ত প্রশ্ন: -
followsণাত্মক সংখ্যাকে সমর্থন করার জন্য আমরা আলগো পরিবর্তন করতে পারি: -
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
আমি মনে করি ব্রায়ান কর্নিগানের পদ্ধতিটিও কার্যকর হবে ... সেট বিট রয়েছে যতটা পুনরাবৃত্তির মধ্য দিয়ে যায়। সুতরাং যদি আমাদের কাছে কেবলমাত্র উচ্চ বিট সেট সহ 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। ১৯ এপ্রিল, ২০০ On-এ ডন নুথ আমাকে ইঙ্গিত করেছিলেন যে এই পদ্ধতিটি "পিটার ওয়েগনার দ্বারা প্রথম প্রকাশিত হয়েছিল সিএসিএম 3 (1960), 322 সালে। এছাড়াও ডেরিক লেহারের দ্বারা স্বাধীনভাবে আবিষ্কার করা হয়েছিল এবং 1964 সালে বেকেনবাচের সম্পাদিত একটি বইতে প্রকাশিত হয়েছিল।"
আমি নীচের কোডটি ব্যবহার করি যা আরও স্বজ্ঞাত।
int countSetBits(int n) {
return !n ? 0 : 1 + countSetBits(n & (n-1));
}
যুক্তি: এন এবং (এন -1) এন এর শেষ সেট বিট পুনরায় সেট করে।
পিএস: আমি জানি এটি একটি ও (1) সমাধান নয়, তবে একটি আকর্ষণীয় সমাধান।
O(ONE-BITS)
। এটি হ'ল ও (1) যেহেতু সর্বাধিক 32 টি বিট রয়েছে।
"সেরা অ্যালগরিদম" এর অর্থ কী? সংক্ষিপ্ত কোড বা দ্রুত কোড? আপনার কোডটি খুব মার্জিত দেখাচ্ছে এবং এটির একটি ধ্রুবক প্রয়োগের সময় রয়েছে। কোডটিও খুব ছোট।
তবে যদি গতিটি প্রধান ফ্যাক্টর এবং কোডের আকার না হয় তবে আমি মনে করি অনুসরণটি আরও দ্রুত হতে পারে:
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 বিট মানটি আরও দ্রুত হতে পারে।
আমি প্রায় 1990 সালে আরআইএসসি মেশিনগুলির জন্য একটি দ্রুত বিটক্টন ম্যাক্রো লিখেছিলাম It এটি উন্নত পাটিগণিত (গুণ, বিভাগ,%), মেমরি ফেচগুলি (খুব ধীরগতির), শাখা (খুব ধীরে ধীরে) ব্যবহার করে না তবে এটি ধরে নেয় যে সিপিইউতে একটি রয়েছে 32-বিট ব্যারেল শিফটার (অন্য কথায়, >> 1 এবং >> 32 একই পরিমাণে চক্র গ্রহণ করে)) এটি ধরে নিয়েছে যে ছোট ধ্রুবকগুলি (যেমন 6, 12, 24) নিবন্ধগুলিতে লোড করার জন্য কিছুই খরচ করে না বা সংরক্ষণ করা হয় অস্থায়ী মধ্যে এবং বার বার পুনরায় ব্যবহৃত।
এই অনুমানগুলি সহ, এটি বেশিরভাগ আরআইএসসি মেশিনে প্রায় 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 ম কলাম (এ) গ্রহণ করি, তবে এটি ডান 1 বিটকে স্থানান্তর করুন এবং এ বি থেকে বিয়োগ করলে আমি আউটপুট (সিডি) পাই। 3 বিট এক্সটেনশন একই; আপনি যদি চান তবে উপরের মতো 8-সারির বুলিয়ান টেবিল দিয়ে এটি পরীক্ষা করতে পারেন।
আপনি যদি সি ++ ব্যবহার করেন তবে অন্য বিকল্পটি হ'ল টেমপ্লেট মেটাগ্রোগ্রামিং ব্যবহার করা:
// 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 )
আপনি অবশ্যই বিভিন্ন ধরণের (এমনকি স্বয়ং-সনাক্তকরণ বিট আকার) ব্যবহার করতে এই টেমপ্লেটটি আরও প্রসারিত করতে পারেন তবে আমি স্পষ্টতার জন্য এটি সহজ রেখেছি।
সম্পাদনা: এটি উল্লেখ করতে ভুলে গেছেন কারণ এটি যে কোনও সি ++ কম্পাইলারে কাজ করা উচিত এবং বিট কাউন্টের জন্য যদি একটি ধ্রুবক মান ব্যবহৃত হয় তবে এটি মূলত আপনার লুপটি আনারোল করে দেয় (অন্য কথায়, আমি খুব নিশ্চিত যে এটি দ্রুততম সাধারণ পদ্ধতি) তুমি খুঁজে পাবে)
constexpr
যদিও একটি সুন্দর করতে পারে ।
আমি ভাগ্য ফাইল থেকে এই উদাহরণটি বিশেষভাবে পছন্দ করি:
# বিট অ্যাকাউন্ট (x) (((বিএক্স_ (এক্স) + (বিএক্স_ (এক্স) >> 4))) এবং 0x0F0F0F0F)% 255) # ডিফাইন বিএক্স_ (এক্স) ((এক্স) - (((এক্স) >> 1) এবং 0x77777777) - (((x) >> 2) এবং 0x33333333) - (((x) >> 3) এবং 0x11111111))
আমি এটি সবচেয়ে ভাল কারণ এটি খুব সুন্দর!
জাভা জেডিকে ১.৫
Integer.bitCount (ঢ);
যেখানে 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);
}
আমি সিমডি নির্দেশাবলী (এসএসএসই 3 এবং এভিএক্স 2) ব্যবহার করে একটি অ্যারেতে বিট কাউন্টিংয়ের একটি বাস্তবায়ন পেয়েছি। এটি __popcnt64 অভ্যন্তরীণ ফাংশনটি ব্যবহার করবে তার চেয়ে এটি 2-2.5 গুণ ভাল পারফরম্যান্সে রয়েছে।
এসএসএসই 3 সংস্করণ:
#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];
}
সেট বিট গণনা করার জন্য অনেক অ্যালগরিদম রয়েছে; তবে আমি মনে করি সেরাটি দ্রুততর! আপনি এই পৃষ্ঠায় বিস্তারিত দেখতে পারেন:
আমি এটি একটি পরামর্শ:
বিট গণনা 14, 24, বা 32-বিট শব্দের মধ্যে 64-বিট নির্দেশাবলী ব্যবহার করে সেট করা হয়েছে
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-বিট সিপিইউ প্রয়োজন। প্রথম বিকল্পটি লাগে মাত্র 3 টি অপারেশন; দ্বিতীয় বিকল্পটি 10 লাগে; এবং তৃতীয় বিকল্পটি লাগে 15।
বাইট বিটের পূর্ব-গণনাযুক্ত টেবিল ব্যবহার করে দ্রুত সি # দ্রষ্টব্য ইনপুট আকারের শাখা প্রশাখার সাথে গণনা করে।
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
};
}
(0xe994 >>(k*2))&3
স্মৃতি অ্যাক্সেস ছাড়াই আপনি একবারে 3 টি বিট 'অনুসন্ধান' করতে পারেন ...
এখানে একটি পোর্টেবল মডিউল (এএনএসআই-সি) দেওয়া হয়েছে যা আপনার প্রতিটি অ্যালগরিদমকে কোনও আর্কিটেকচারে বেনমার্ক করতে পারে।
আপনার সিপিইউতে 9 বিট বাইট রয়েছে? কোনও সমস্যা নেই :-) এই মুহুর্তে এটি 2 টি অ্যালগরিদম, কেএন্ডআর অ্যালগরিদম এবং একটি বাইট অনুসারে অনুসন্ধানের সারণী প্রয়োগ করে। দেখার টেবিলটি কে ও আর অ্যালগরিদমের তুলনায় গড়ে তিনগুণ দ্রুত। যদি কেউ "হ্যাকারস ডিলাইট" অ্যালগরিদম বহনযোগ্য কোনও বানাতে কোনও উপায় আবিষ্কার করতে পারে তবে তা এটিকে বিনা দ্বিধায় যোগ করতে পারেন।
#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
আপনি যা করতে পারেন তা হ'ল
while(n){
n=n&(n-1);
count++;
}
এর পিছনে যুক্তিটি হ'ল এন -1 এর বিটগুলি এন এর ডানদিকের সেট বিট থেকে উল্টানো হয়। যদি এন = ie অর্থাৎ ১১০ হয় তবে ৫ টি 101 এর বিটগুলি এন এর ডানদিকের সেট বিট থেকে উল্টানো হবে। সুতরাং আমরা এবং এই দুটি যদি আমরা প্রতিটি পুনরাবৃত্তিতে ডানদিকের বিট 0 করব এবং সর্বদা পরবর্তী ডানদিকের সেট বিটটিতে যাব, সেট বিটটি গণনা করছি worst প্রতিটা বিট সেট হয়ে গেলে সবচেয়ে খারাপ সময় জটিলতা ও (লগইন) হবে।