Dipahami bahwa kasus terburuknya adalah O(N)
, ada beberapa optimasi mikro yang sangat bagus.
Metode naif melakukan perbandingan karakter dan perbandingan akhir teks untuk setiap karakter.
Menggunakan sentinel (yaitu salinan karakter target di akhir teks) mengurangi jumlah perbandingan hingga 1 per karakter.
Pada level twiddling ada:
#define haszero(v) ( ((v) - 0x01010101UL) & ~(v) & 0x80808080UL )
#define hasvalue(x, n) ( haszero((x) ^ (~0UL / 255 * (n))) )
untuk mengetahui apakah byte dalam kata ( x
) memiliki nilai tertentu ( n
).
Subekspresi v - 0x01010101UL
, dievaluasi menjadi bit tinggi yang diatur dalam byte mana pun setiap kali byte yang sesuai dalam v
adalah nol atau lebih besar dari 0x80
.
Sub-ekspresi ~v & 0x80808080UL
mengevaluasi bit tinggi yang diatur dalam byte di mana byte v
tidak memiliki bit set yang tinggi (sehingga byte kurang dari 0x80
).
Dengan ANDing dua sub-ekspresi ini ( haszero
) hasilnya adalah bit tinggi yang ditetapkan di mana byte dalam v
nol, karena bit tinggi yang ditetapkan karena nilai yang lebih besar daripada 0x80
dalam sub-ekspresi pertama ditutup oleh yang kedua (27 April, 1987 oleh Alan Mycroft).
Sekarang kita dapat XOR nilai untuk diuji ( x
) dengan kata yang telah diisi dengan nilai byte yang kita minati ( n
). Karena XORing nilai dengan itu sendiri menghasilkan byte nol dan bukan nol sebaliknya, kita dapat meneruskan hasilnya haszero
.
Ini sering digunakan dalam strchr
implementasi yang khas .
(Stephen M Bennet menyarankan ini pada 13 Desember 2009. Rincian lebih lanjut dalam Bit Twiddling Hacks yang terkenal ).
PS
kode ini rusak untuk kombinasi apa pun 1111
di sebelah a0
Retasan melewati tes brute force (bersabarlah):
#include <iostream>
#include <limits>
bool haszero(std::uint32_t v)
{
return (v - std::uint32_t(0x01010101)) & ~v & std::uint32_t(0x80808080);
}
bool hasvalue(std::uint32_t x, unsigned char n)
{
return haszero(x ^ (~std::uint32_t(0) / 255 * n));
}
bool hasvalue_slow(std::uint32_t x, unsigned char n)
{
for (unsigned i(0); i < 32; i += 8)
if (((x >> i) & 0xFF) == n)
return true;
return false;
}
int main()
{
const std::uint64_t stop(std::numeric_limits<std::uint32_t>::max());
for (unsigned c(0); c < 256; ++c)
{
std::cout << "Testing " << c << std::endl;
for (std::uint64_t w(0); w != stop; ++w)
{
if (w && w % 100000000 == 0)
std::cout << w * 100 / stop << "%\r" << std::flush;
const bool h(hasvalue(w, c));
const bool hs(hasvalue_slow(w, c));
if (h != hs)
std::cerr << "hasvalue(" << w << ',' << c << ") is " << h << '\n';
}
}
return 0;
}
Banyak upvotes untuk jawaban yang membuat asumsi satu karakter = satu byte, yang saat ini bukan standar lagi
Terima kasih atas komentarnya.
Jawabannya dimaksudkan untuk apa pun kecuali esai tentang pengkodean multi-byte / variabel-lebar :-) (dalam semua keadilan itu bukan bidang keahlian saya dan saya tidak yakin itu yang dicari OP).
Pokoknya menurut saya ide / trik di atas agak bisa disesuaikan dengan MBE (terutama penyandian sinkronisasi sendiri ):
- seperti dicatat dalam komentar Johan, hack dapat 'dengan mudah' diperluas untuk bekerja dengan byte ganda atau apa pun (tentu saja Anda tidak dapat merentangkannya terlalu banyak);
- fungsi khas yang menempatkan karakter dalam string karakter multibyte:
- teknik sentinel dapat digunakan dengan sedikit tinjauan ke depan.