7 sayısını temsil eden 8 bit şu şekildedir:
00000111
Üç bit ayarlanmıştır.
32 bit tam sayıdaki set bitlerinin sayısını belirlemek için algoritmalar nelerdir?
7 sayısını temsil eden 8 bit şu şekildedir:
00000111
Üç bit ayarlanmıştır.
32 bit tam sayıdaki set bitlerinin sayısını belirlemek için algoritmalar nelerdir?
Yanıtlar:
Bu, ' Hamming Weight ', ' Hamming Weight ', 'popcount' veya 'yandan toplama' olarak bilinir .
'En iyi' algoritma gerçekten hangi CPU'da olduğunuza ve kullanım düzeninizin ne olduğuna bağlıdır.
Bazı CPU'ların bunu yapmak için tek bir yerleşik talimatı vardır ve diğerlerinde bit vektörleri üzerinde çalışan paralel talimatlar vardır. Paralel talimatlar ( popcnt
desteklendiği CPU'larda x86 gibi ) neredeyse kesinlikle en hızlı olacaktır. Diğer bazı mimariler, döngü başına biraz test eden mikrokodik bir döngü ile uygulanan yavaş bir talimata sahip olabilir ( alıntı gerekir ).
Önceden doldurulmuş bir tablo arama yöntemi, CPU'nuz büyük bir önbelleğe sahipse ve / veya bu talimatların çoğunu sıkı bir döngüde gerçekleştiriyorsanız çok hızlı olabilir. Ancak CPU'nun tablonun bir kısmını ana bellekten alması gereken bir 'önbellek kaçışının' maliyeti nedeniyle zarar görebilir. (Tabloyu küçük tutmak için her baytı ayrı ayrı arayın.)
Baytlarınızın çoğunlukla 0 veya çoğunlukla 1 olacağını biliyorsanız, bu senaryolar için çok etkili algoritmalar vardır.
Çok iyi bir genel amaçlı algoritmanın 'paralel' veya 'değişken hassasiyetli SWAR algoritması' olarak bilinen aşağıdaki olduğuna inanıyorum. Bunu C benzeri bir sahte dilde ifade ettim, belirli bir dil (örneğin C ++ için uint32_t ve Java'da >>> kullanarak) için ayarlamanız gerekebilir:
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;
}
JavaScript için: tamsayıya zorlamak ile |0
performans için: İlk satırı değiştirini = (i|0) - ((i >> 1) & 0x55555555);
Bu, tartışılan herhangi bir algoritmanın en kötü durum davranışına sahiptir, bu nedenle attığınız herhangi bir kullanım şekli veya değeri ile etkili bir şekilde ilgilenir.
i = i - ((i >> 1) & 0x55555555);
İlk adım, tek / çift bitleri izole etmek, sıralamak için değiştirmek ve eklemek için maskelemenin optimize edilmiş bir versiyonudur. Bu, 2 bit akümülatörlerde 16 ayrı ekleme yapar ( SWAR = Bir Kayıt İçinde SIMD ). Gibi (i & 0x55555555) + ((i>>1) & 0x55555555)
.
Bir sonraki adım, bu 16x 2 bit akümülatörlerin tek / hatta sekizini alır ve tekrar ekleyerek 8x 4 bit toplamları üretir. i - ...
Sadece değişen önce / sonra maske bu yüzden optimizasyon bu kez mümkün değildir. Kaydırmalarda 32 bit sabitleri ayrı ayrı oluşturması gereken ISA'lar için derleme yaparken aynı 0x33...
sabiti iki kez kullanmak 0xccc...
iyi bir şeydir.
Son kaydırma ve ekleme adımı (i + (i >> 4)) & 0x0F0F0F0F
4x 8 bit akümülatörlere genişler. Bu maske daha sonra herhangi bir 4 bit toplayıcı maksimum değer olduğundan, daha önce ilave edilmesi yerine 4
, karşılık gelen giriş uçları her 4 bit ayarlanmış olması halinde. 4 + 4 = 8 hala 4 bite sığar, bu yüzden kırıntı elemanları arasında taşımak imkansızdır i + (i >> 4)
.
Şimdiye kadar bu, birkaç akıllı optimizasyonla SWAR tekniklerini kullanan oldukça normal bir SIMD'dir. Aynı desenle 2 adım daha devam etmek, 2x 16-bit'e sonra 1x 32-bit sayılarına kadar genişleyebilir. Ancak hızlı donanıma sahip makinelerde daha verimli bir yol var:
Yeterli sayıda "elemana" sahip olduğumuzda, büyü sabiti ile çarpma tüm öğeleri üst elemana toplayabilir . Bu durumda bayt elemanları. Çarpma, sola kaydırma ve ekleme ile yapılır, böylece sonuçları çoğaltın . x * 0x01010101
x + (x<<8) + (x<<16) + (x<<24)
Bizim 8 bit elemanları yeterince geniş (ve yeterince küçük sayımları tutarak) bu eldeyi üretmek edilmemesidir içine o üst 8 bit.
Bunun 64 bit sürümü, 0x0101010101010101 çarpanıyla 64 bit tamsayıda 8x 8 bit öğe yapabilir ve yüksek baytı ile ayıklayabilir >>56
. Yani fazladan bir adım atmıyor, sadece daha geniş sabitler. __builtin_popcountll
Donanım popcnt
talimatı etkinleştirilmediğinde GCC x86 sistemlerinde kullanır . Bunun için builtins veya intrinsics kullanabiliyorsanız, derleyiciye hedefe özgü optimizasyon yapma şansı verin.
Bu bitsel-SWAR algoritması, SIMD'li, ancak kullanılabilir popcount komutu olmayan CPU'larda bir hızlanma için, tek bir tamsayı yazmacı yerine, birden fazla vektör öğesinde aynı anda yapılabilecek şekilde paralelleştirilebilir. (örn. Nehalem veya sonraki sürümlerinde değil, herhangi bir CPU'da çalışması gereken x86-64 kodu.)
Bununla birlikte, popcount için vektör talimatlarını kullanmanın en iyi yolu, genellikle her bir baytın paralel olarak 4 bit için bir tablo araması yapmak için değişken bir shuffle kullanmaktır. (4 bit, bir vektör yazmacında tutulan 16 giriş tablosunu indeksler).
Intel CPU'lar üzerinde bir performans gösterebilirler donanım 64bit popcnt talimat SSSE3 PSHUFB
uygulanmasını paralel bit 2 faktörüyle ilgili tarafından, ama sadece sizin derleyici sadece sağ alırsa . Aksi takdirde SSE önemli ölçüde öne çıkabilir. Daha yeni derleyici sürümleri, Intel'deki popcnt yanlış bağımlılık sorununun farkındadır .
Referanslar:
unsigned int
, herhangi bir işaret biti komplikasyonundan arınmış olduğunu kolayca göstermek için kullanmalıdır . Ayrıca uint32_t
, tüm platformlarda beklediğiniz gibi, daha güvenli olur mu?
>>
negatif değerler için uygulama tanımlıdır. Argümanın değiştirilmesi (veya yayınlanması) gerekir unsigned
ve kod 32 bit'e özgü olduğundan, muhtemelen kullanıyor olmalıdır uint32_t
.
Ayrıca derleyicilerinizin yerleşik işlevlerini de göz önünde bulundurun.
Örneğin GNU derleyicisinde şunları kullanabilirsiniz:
int __builtin_popcount (unsigned int x);
int __builtin_popcountll (unsigned long long x);
En kötü durumda derleyici bir işleve çağrı oluşturur. En iyi durumda derleyici aynı işi daha hızlı yapmak için bir cpu talimatı yayar.
GCC intrinsikleri birden fazla platformda bile çalışır. Popcount, x86 mimarisinde ana akım olacak, bu yüzden şimdi içsel kullanmaya başlamak mantıklı. Diğer mimariler yıllardır popcount'a sahiptir.
X86'da, derleyiciye , aynı nesilde eklenen vektör komutlarıyla popcnt
komut için -mpopcnt
veya -msse4.2
aynı zamanda etkinleştirmek için destek alabileceğini söyleyebilirsiniz . GCC x86 seçeneklerine bakın . -march=nehalem
(veya -march=
kodunuzun varsaymasını ve ayarlamasını istediğiniz CPU ne olursa olsun) iyi bir seçim olabilir. Ortaya çıkan ikili dosyayı daha eski bir CPU'da çalıştırmak geçersiz talimat hatasına neden olur.
-march=native
İkilileri, üzerine kurduğunuz makine için optimize etmek için (gcc, clang veya ICC ile) kullanın.
MSVC, x86 popcnt
komutu için gerçek bir özellik sağlar , ancak gcc'den farklı olarak, gerçekten de donanım eğitimi için gerçek bir özelliktir ve donanım desteği gerektirir.
Yerleşik std::bitset<>::count()
yerine kullanma
Teorik olarak, hedef CPU için verimli bir şekilde nasıl hesaplanacağını bilen herhangi bir derleyici, bu işlevselliği ISO C ++ ile ortaya çıkarmalıdır std::bitset<>
. Pratikte, bazı durumlarda bazı hedef CPU'lar için bit-hack AND / shift / ADD ile daha iyi olabilirsiniz.
Donanım popcount'unun isteğe bağlı bir uzantı (x86 gibi) olduğu hedef mimariler için, tüm derleyiciler std::bitset
mevcut olduğunda bundan yararlanan bir mimariye sahip değildir . Örneğin, msvc etkinleştirmek için bir yol vardır popcnt
derleme zamanında destek ve her zaman kullanan bir tablo arama olsa bile, /Ox /arch:AVX
(teknik için ayrı bir özelliği biraz olsa, SSE4.2 ima popcnt
).
Ancak en azından her yerde çalışan taşınabilir bir şey elde edersiniz ve doğru hedef seçeneklerine sahip gcc / clang ile, onu destekleyen mimariler için donanım popcount'u alırsınız.
#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();
}
Bkz gcc, clang, icc ve MSVC gelen asm Godbolt derleyici kaşif üzerinde.
x86-64 bunu gcc -O3 -std=gnu++11 -mpopcnt
yayar:
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
yayıyor ( int
arg sürümü için):
rldicl 3,3,0,32 # zero-extend from 32 to 64-bit
popcntd 3,3 # popcount
blr
Bu kaynak hiç x86'ya veya GNU'ya özgü değildir, ancak yalnızca gcc / clang / icc içeren x86 için iyi derler.
Ayrıca, gcc'nin tek komutlu popcount içermeyen mimariler için geri dönüşünün bir kerede bir bayt tablo araması olduğunu unutmayın. Örneğin, ARM için bu harika değil .
std::bitset::count
. bu satır aradan sonra tek bir __builtin_popcount
çağrıyı derler .
Benim düşünceme göre, "en iyi" çözüm, bolca yorum yapmadan başka bir programcı (veya iki yıl sonra orijinal programcı) tarafından okunabilen çözümdür. Bazılarının zaten sağladığı en hızlı veya en akıllı çözümü isteyebilirsiniz, ancak her zaman akıllılığa göre okunabilirliği tercih ederim.
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;
}
Daha fazla hız istiyorsanız (ve haleflerinize yardımcı olmak için bunu iyi bir şekilde belgelediğinizi varsayarsanız) bir tablo araması kullanabilirsiniz:
// 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);
}
Her ne kadar bunlar belirli veri türü boyutlarına bağlı olsa da o kadar taşınabilir değillerdir. Ancak, birçok performans optimizasyonu zaten taşınabilir olmadığından, bu bir sorun olmayabilir. Taşınabilirlik istiyorsanız, okunabilir çözüme sadık kalırdım.
if ((value & 1) == 1) { count++; }
ile count += value & 1
?
Hacker'ın Lokumundan, s. 66, Şekil 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-ish talimatları (ark bağımlı), dallanma olmadan yürütür.
Hacker'ın Delight olduğunu enfes! Şiddetle tavsiye edilir.
Integer.bitCount(int)
aynı tam uygulamayı kullanır.
pop
yerine population_count
(ya pop_cnt
da bir kısaltma olması gerekir) bunu arayan herkese iyi bir tekme vermek istiyorum. @MarcoBolis Java'nın tüm sürümleri için geçerli olacağını, ancak resmen uygulamaya bağlı olacağını düşünüyorum :)
Arama tablolarını ve popcount'u kullanmadan en hızlı yolun şu olduğunu düşünüyorum . Sadece 12 işlemle set bitlerini sayar.
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;
}
Çalışır, çünkü iki yarıya bölünerek, her iki yarıdaki set bitlerinin sayısını sayarak ve daha sonra toplayarak toplam set bitlerini sayabilirsiniz. Divide and Conquer
Paradigma olarak da bilin . Ayrıntılara girelim ..
v = v - ((v >> 1) & 0x55555555);
İki bit bit sayısı olabilir 0b00
, 0b01
ya da 0b10
. Bunu 2 bit üzerinde çözmeye çalışalım ..
---------------------------------------------
| v | (v >> 1) & 0b0101 | v - x |
---------------------------------------------
0b00 0b00 0b00
0b01 0b00 0b01
0b10 0b01 0b01
0b11 0b01 0b10
Gerekli olan buydu: son sütun her iki bit çiftindeki ayarlanmış bit sayısını gösterir. İki bitlik sayı ise >= 2 (0b10)
o zaman and
üretir 0b01
, başka ürettiği 0b00
.
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
Bu ifadenin anlaşılması kolay olmalıdır. İlk işlemden sonra her iki bitte ayarlanmış bit sayısı vardır, şimdi bu sayımı her 4 bitte özetliyoruz.
v & 0b00110011 //masks out even two bits
(v >> 2) & 0b00110011 // masks out odd two bits
Daha sonra yukarıdaki sonucu toplayarak bize 4 bitlik toplam set biti sayısını veririz. Son ifade en zor olanıdır.
c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
Hadi biraz daha yıkalım ...
v + (v >> 4)
İkinci ifadeye benzer; set bitlerini 4'lü gruplar halinde sayıyoruz. Önceki operasyonlarımız nedeniyle her nibble'ın içinde ayarlanmış bit sayısı olduğunu biliyoruz. Bir örnek verelim. Diyelim ki baytımız var 0b01000010
. Bu, ilk kemirgenin 4 biti ve ikincisinin 2 biti olduğu anlamına gelir. Şimdi bu kemikleri birlikte ekliyoruz.
0b01000010 + 0b01000000
Bize ilk bayttaki bir bayttaki set bitlerinin sayısını verir 0b01100010
ve bu nedenle sayıdaki tüm baytların son dört baytını maskeleriz (onları atarız).
0b01100010 & 0xF0 = 0b01100000
Şimdi her baytın içinde ayarlanmış bit sayısı vardır. Hepsini bir araya getirmemiz gerekiyor. İşin püf noktası sonucu0b10101010
ilginç bir özelliğe sahip . Sayımızın dört baytı varsa, A B C D
bu baytlarla yeni bir sayıya neden olur A+B+C+D B+C+D C+D D
. 4 baytlık bir sayı en fazla 32 bit olarak ayarlanabilir ve bu sayı olarak temsil edilebilir 0b00100000
.
Şimdi ihtiyacımız olan tek şey, tüm baytlardaki tüm set bitlerinin toplamına sahip olan ilk bayttır ve bunu elde ederiz >> 24
. Bu algoritma 32 bit
kelimeler için tasarlanmıştır ancak kelimeler için kolayca değiştirilebilir 64 bit
.
c =
hakkında? Görünüşe göre ortadan kaldırılmalıdır. Ayrıca, bazı klasik uyarıları önlemek için ekstra bir A "(((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24" paren seti önerin.
popcount(int v)
ve hem de için çalışmasıdır popcount(unsigned v)
. Taşınabilirlik için düşünün popcount(uint32_t v)
, vb. Gerçekten * 0x1010101 bölümü gibi.
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
bu yüzden aslında ne yaptığınızı görmek için harfleri saymamız gerekmez (ilkini attığınızdan, 0
yanlışlıkla yanlış (ters çevrilmiş) bit desenini maske olarak kullandığınızı düşündüm - yani 8 değil sadece 7 harf olduğunu fark edene kadar).
Sıkıldım ve üç yaklaşımın milyarlarca yinelemesini zamanladım. Derleyici gcc -O3. CPU, 1. nesil Macbook Pro'ya koydukları şeydir.
3.7 saniyede en hızlısı şudur:
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] );
}
İkincisi aynı koda gider ancak 2 yarım kelime yerine 4 bayt arar. Bu yaklaşık 5.5 saniye sürdü.
Üçüncülük, 8,6 saniye süren bit-twiddling 'yandan toplama' yaklaşımına gidiyor.
Dördüncülük, 11 saniye içinde GCC'nin __builtin_popcount () yöntemine gider.
Sayım birer birer yaklaşım daha yavaştı ve tamamlanmasını beklemekten sıkıldım.
Performansı her şeyden önce önemsiyorsanız, ilk yaklaşımı kullanın. Eğer ilgileniyorsanız, ancak 64Kb RAM harcamak için yeterli değilse, ikinci yaklaşımı kullanın. Aksi takdirde, her seferinde okunabilir (ancak yavaş) bir bit yaklaşımı kullanın.
Bit çevirme yaklaşımını kullanmak istediğiniz bir durumu düşünmek zor.
Düzenleme: Burada benzer sonuçlar .
Java kullanıyorsanız, yerleşik yöntem Integer.bitCount
bunu yapar.
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;
}
Bu algoritmayı açıklayayım.
Bu algoritma Bölme ve Conquer Algoritmasına dayanır. Bir 8 bit tam sayı 213 (ikili dosyada 11010101) olduğunu varsayalım, algoritma şu şekilde çalışır (her seferinde iki komşu bloğu birleştirir):
+-------------------------------+
| 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)
+-------------------------------+
Bu, mikro mimarinizi bilmeye yardımcı olduğu sorulardan biridir. Ben sadece gcc 4.3.3 altında iki değişken zamanlama fonksiyon çağrısı yükü, bir milyar yinelemeleri ortadan kaldırmak için C ++ satırları kullanarak derlenmiş, zamanlayıcı için rdtsc kullanarak, önemli bir şey kaldırmak için tüm sayımların çalışma tutarını tutarak ( saat çevrimi hassas).
satır içi int pop2 (işaretsiz x, işaretsiz 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); dönüş (x + y) & 0x000000FF; }
Değiştirilmemiş Hacker'ın Lokumu 12.2 gigacycle aldı. Paralel versiyonum (iki bit kadar sayma) 13.0 gigacycles ile çalışıyor. 2.4GHz Core Duo'da her ikisi için toplam 10.5s geçti. 25 gigacycles = bu saat frekansında 10 saniyenin biraz üzerinde, bu yüzden zamanlamalarımın doğru olduğundan eminim.
Bu, bu algoritma için çok kötü olan talimat bağımlılık zincirleriyle ilgilidir. Bir çift 64 bit kayıt kullanarak hızı neredeyse iki katına çıkarabilirdim. Aslında, eğer zeki olsaydım ve x + ya biraz daha eklersem, bazı vardiyaları tıraş edebilirdim. Bazı küçük ayarlamalar ile 64-bit sürümü eşit çıkacaktı, ancak tekrar iki kat daha fazla sayılacak.
128 bit SIMD kayıtları ile, yine bir başka faktör ve SSE komut setleri de genellikle akıllı kısa yollara sahiptir.
Kodun özellikle şeffaf olması için hiçbir neden yoktur. Arayüz basittir, algoritmaya birçok yerde çevrimiçi olarak başvurulabilir ve kapsamlı birim testine uygundur. Tökezleyen programcı bir şeyler bile öğrenebilir. Bu bit işlemleri makine düzeyinde son derece doğaldır.
Tamam, tweaked 64-bit versiyonunu karşılaştırmaya karar verdim. Bunun için sizeof (unsigned long) == 8
satır içi int pop2 (işaretsiz uzun x, işaretsiz uzun 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); dönüş x & 0xFF; }
Bu doğru görünüyor (Yine de dikkatle test etmiyorum). Şimdi zamanlamalar 10.70 gigacycles / 14.1 gigacycles olarak çıkıyor. Daha sonraki sayı 128 milyar biti topladı ve bu makinede geçen 5.9'lara karşılık geldi. 64-bit modunda çalıştığım ve 64-bit kayıtları 32-bit kayıtlardan biraz daha iyi olduğu için paralel olmayan versiyon biraz hızlanıyor.
Bakalım burada biraz daha fazla OOO boru hattı var mı. Bu biraz daha ilgiliydi, bu yüzden aslında biraz test ettim. Her terim tek başına 64'tür, hepsi toplam 256'dır.
satır içi int pop4 (işaretsiz uzun x, işaretsiz uzun y, imzasız uzun u, imzasız uzun 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); dönüş x & 0x000001FF; }
Bir an için heyecanlandım, ancak bazı testlerde inline anahtar kelimesini kullanmama rağmen gcc -O3 ile satır içi hileler oynuyor. Gcc'nin hile yapmasına izin verdiğimde, bir milyar pop4 () çağrısı 12.56 gigacycles alır, ancak bunun sabit ifadeler olarak argümanları katladığını belirledim. Daha gerçekçi bir sayı,% 30'luk bir hız artışı için 19.6gc gibi görünüyor. Test döngüm şimdi şuna benziyor, her bir argümanın gcc'nin hile yapmasını engelleyecek kadar farklı olduğundan emin olun.
hitime b4 = rdtsc (); için (işaretsiz uzun i = 10L * 1000 * 1000 * 1000; i <11L * 1000 * 1000 * 1000; ++ i) sum + = pop4 (i, i ^ 1, ~ i, i | 1); hitime e4 = rdtsc ();
8.17'lerde toplanan 256 milyar bit geçti. 16-bit tablo aramasında kıyaslandığı gibi 32 milyon bit için 1.02s'ye kadar çalışır. Doğrudan karşılaştırılamıyor, çünkü diğer tezgah saat hızı vermiyor, ancak LK önbelleğinin trajik bir kullanımı olan 64KB tablo sürümünün sümüğünü tokatladım gibi görünüyor.
Güncelleme: dört tane daha yinelenen satır ekleyerek bariz yapmaya ve pop6 () oluşturmaya karar verdi. 22.8 gc, 384 milyar bit toplam 9.5s toplandı geçti. 32 milyar bit için 800ms'de% 20 daha var.
Neden yinelemeli olarak 2'ye bölmüyorsunuz?
sayım = 0 n> 0 iken eğer (n% 2) == 1 sayım + = 1 n / = 2
Bunun en hızlı olmadığını, ancak "en iyi" nin biraz belirsiz olduğunu kabul ediyorum. "En iyisinin" bir açıklık unsuru olması gerektiğini savunuyorum
Hacker'ın Delight bit döndürmesi, bit kalıplarını yazdığınızda çok daha net hale gelir.
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;
}
İlk adım, tek bitlere çift bitleri ekleyerek her ikisinde bir miktar bit üretir. Diğer adımlar, tüm int'i alan son sayım elde edene kadar, düşük sıralı parçalara yüksek sıralı parçalar ekler.
2 32 arama tablosu arasında ve her bir bit boyunca ayrı ayrı yineleme yapan mutlu bir ortam için :
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;
}
Gönderen http://ctips.pbwiki.com/CountBits
Bu, ayarlanan bit sayısının O(k)
olduğu yerde yapılabilir k
.
int NumberOfSetBits(int n)
{
int count = 0;
while (n){
++ count;
n = (n - 1) & n;
}
return count;
}
n &= (n-1)
formu kullandığı küçük değişiklikle .
Bu en hızlı veya en iyi çözüm değil, ama aynı soruyu kendi yolumda buldum ve düşünmeye ve düşünmeye başladım. Sonunda, problemi matematik tarafından alıp bir grafik çizerseniz, bunun böyle yapılabileceğini fark ettim, o zaman bunun periyodik bir kısmı olan bir fonksiyon olduğunu görüyorsunuz ve sonra dönemler arasındaki farkı fark ediyorsunuz ... Hadi bakalım:
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))()
Aradığınız işleve genellikle ikili sayının "yan toplamı" veya "nüfus sayısı" denir. Knuth bunu Fasikül 1A, ss11-12'de tartışıyor (Cilt 2, 4.6.3- (7) 'de kısa bir referans olmasına rağmen)
Coğrafyası Peter Wegner'in makale "Bir İkili Bilgisayar Sayma Ones için bir Tekniği" dir ACM Communications , Cilt 3 (1960) Sayı 5, sayfa 322 . Orada iki farklı algoritma veriyor, bunlardan biri "seyrek" olması beklenen sayılar için optimize edilmiş (yani az sayıda algoritmaya sahip) ve diğeri ise tersi durumda.
Birkaç açık soru: -
aşağıdaki gibi negatif sayıyı desteklemek için algoyu değiştirebiliriz:
count = 0
while n != 0
if ((n % 2) == 1 || (n % 2) == -1
count += 1
n /= 2
return count
şimdi ikinci sorunun üstesinden gelmek için algo gibi yazabiliriz: -
int bit_count(int num)
{
int count=0;
while(num)
{
num=(num)&(num-1);
count++;
}
return count;
}
tam referans için bakınız:
http://goursaha.freeoda.com/Miscellaneous/IntegerBitCount.html
Bence Brian Kernighan'ın yöntemi de faydalı olacak ... Belirlenen bitler kadar çok yinelemeden geçiyor. Yani sadece yüksek biti ayarlanmış 32 bitlik bir kelimemiz varsa, o zaman döngüden sadece bir kez geçer.
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'de yayınlanan C Programlama Dili 2. Baskı. (Brian W. Kernighan ve Dennis M. Ritchie tarafından) 2-9. 19 Nisan 2006'da Don Knuth bana bu yöntemin "ilk olarak Peter Wegner tarafından CACM 3 (1960), 322'de yayınlandığını belirtti. (Ayrıca Derrick Lehmer tarafından bağımsız olarak keşfedildi ve 1964'te Beckenbach tarafından düzenlenen bir kitapta yayınlandı."
Ben daha sezgisel aşağıdaki kodu kullanın.
int countSetBits(int n) {
return !n ? 0 : 1 + countSetBits(n & (n-1));
}
Mantık: n & (n-1) n'nin son ayarlanan bitini sıfırlar.
PS: Bunun ilginç bir çözüm de olsa O (1) çözümü olmadığını biliyorum.
O(ONE-BITS)
. Gerçekten de O (1) 'dir, çünkü en fazla 32 tek bit vardır.
"En iyi algoritma" ile ne demek istiyorsun? Kısa devre veya açlık kodu? Kodunuz çok zarif görünüyor ve sürekli bir yürütme süresi var. Kod da çok kısadır.
Ancak hız kod boyutu değil, büyük faktör ise, o zaman takip daha hızlı olabilir düşünüyorum:
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 );
}
Bunun 64 bit değer için daha hızlı olmayacağını, ancak 32 bit değerin daha hızlı olabileceğini düşünüyorum.
Yaklaşık 1990'da RISC makineleri için hızlı bir bitcount makro yazdım. Gelişmiş aritmetik (çarpma, bölme,%), bellek getirme (çok yavaş), dallar (çok yavaş) kullanmıyor, ancak CPU'nun 32-bit varil değiştirici (diğer bir deyişle, >> 1 ve >> 32 aynı döngüleri alır.) Küçük sabitlerin (6, 12, 24 gibi) kayıtlara yüklenmesinin bir maliyeti olmadığını veya depolandığını varsayar. ve tekrar tekrar kullandılar.
Bu varsayımlarla, çoğu RISC makinesinde yaklaşık 16 döngü / talimatta 32 bit sayar. 15 komutun / döngünün, döngü veya talimat sayısında bir alt sınıra yakın olduğuna dikkat edin, çünkü ekleme sayısını yarıya indirmek için en az 3 talimat (maske, vardiya, operatör) gibi görünüyor, bu nedenle log_2 (32) = 5, 5 x 3 = 15 talimatı yarı-düşüktür.
#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;
İşte ilk ve en karmaşık adımın bir sırrı:
input output
AB CD Note
00 00 = AB
01 01 = AB
10 01 = AB - (A >> 1) & 0x1
11 10 = AB - (A >> 1) & 0x1
eğer yukarıdaki 1. sütunu (A) alırsam, 1 bit sağa kaydırır ve AB'den çıkarırsam, çıktıyı (CD) alırım. 3 bite genişletme benzerdir; isterseniz benimki gibi 8 sıralı bir boole masa ile kontrol edebilirsiniz.
C ++ kullanıyorsanız başka bir seçenek de şablon meta programlaması kullanmaktır:
// 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;
}
kullanımı:
// 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 )
Tabii ki bu şablonu farklı türleri (hatta otomatik olarak algılayan bit boyutunu) kullanmak için daha da genişletebilirsiniz, ancak netlik için basit tuttum.
edit: herhangi bir C ++ derleyicide çalışması gerekir ve temelde sadece bit sayısı için sabit bir değer kullanılırsa sizin için döngü açar (bu başka bir deyişle, en hızlı genel yöntem olduğundan eminim) iyi bahsetmeyi unuttum bulacaksın)
constexpr
Yine de güzel olabilir .
Özellikle servet dosyasından bu örneğe düşkünüm:
#define BITCOUNT (x) (((BX_ (x) + (BX_ (x) >> 4)) & 0x0F0F0F0F)% 255) #define BX_ (x) [(x) - [((x) >> 1) & 0x77777777) - (((x) >> 2) ve 0x33333333) - (((x) >> 3) ve 0x11111111))
En çok hoşuma gidiyor çünkü çok güzel!
Java JDK1.5
Integer.bitCount (n);
burada n, 1'leri sayılacak olan sayıdır.
ayrıca kontrol et,
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);
}
Ben SIMD talimatı (SSSE3 ve AVX2) kullanarak bir dizide bit sayma bir uygulama bulundu. __Popcnt64 iç işlevini kullanmasından daha 2-2,5 kat daha iyi bir performansa sahiptir.
SSSE3 sürümü:
#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 sürümü:
#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];
}
Set bitlerini saymak için birçok algoritma vardır; ama en iyisinin daha hızlı olduğunu düşünüyorum! Ayrıntılı bilgileri bu sayfada görebilirsiniz:
Bunu öneririm:
64 bit talimatları kullanarak 14, 24 veya 32 bit sözcüklerle ayarlanan bitleri sayma
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;
Bu yöntem, verimli olmak için hızlı modül bölmeli 64 bit CPU gerektirir. İlk seçenek sadece 3 işlem gerektirir; ikinci seçenek 10 alır; ve üçüncü seçenek 15 alır.
Önceden hesaplanmış Byte bit sayım tablosunu kullanarak giriş boyutunda dallanma ile hızlı C # çözümü.
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
Hafıza erişimi olmadan , bir seferde 3 bit 'arayabilirsiniz'
İşte algoritmalarınızın her birini herhangi bir mimaride karşılaştırabilen taşınabilir bir modül (ANSI-C).
CPU'nuzda 9 bit bayt var mı? Sorun değil :-) Şu anda 2 algoritma, K&R algoritması ve bir byte wise arama tablosu uygulamaktadır. Arama tablosu, K&R algoritmasından ortalama 3 kat daha hızlıdır. Birisi "Hacker's Delight" algoritmasını taşınabilir hale getirmenin bir yolunu bulabilirse onu eklemekten çekinmeyin.
#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
ne yapabilirsin
while(n){
n=n&(n-1);
count++;
}
bunun arkasındaki mantık n-1 bitlerinin en sağdaki n bitinden ters çevrilmiş olmasıdır. n = 6 yani 110 ise 5 101'dir, bitler en sağdaki n bitinden ters çevrilir. eğer biz ve bu ikimiz her yinelemede en sağdaki bit 0'ı yapacağız ve her zaman bir sonraki en sağdaki bite gideceğiz.