Resta inteso che il caso peggiore è O(N)
, ci sono alcune micro-ottimizzazioni molto belle.
Il metodo naive esegue un confronto di caratteri e un confronto di fine testo per ciascun carattere.
L'utilizzo di un sentinel (ovvero una copia del carattere di destinazione alla fine del testo) riduce il numero di confronti a 1 per carattere.
A livello di bit twiddling c'è:
#define haszero(v) ( ((v) - 0x01010101UL) & ~(v) & 0x80808080UL )
#define hasvalue(x, n) ( haszero((x) ^ (~0UL / 255 * (n))) )
per sapere se qualsiasi byte in una parola ( x
) ha un valore specifico ( n
).
La sottoespressione v - 0x01010101UL
, valuta un bit elevato impostato in un byte qualsiasi quando il byte corrispondente in v
è zero o maggiore di 0x80
.
La sottoespressione ~v & 0x80808080UL
valuta i bit alti impostati in byte dove il byte di v
non ha il suo bit di bit elevato (quindi il byte era inferiore a 0x80
).
Con ANDing queste due sottoespressioni ( haszero
) il risultato sono i bit alti impostati dove i byte in v
erano zero, poiché i bit alti sono stati impostati a causa di un valore maggiore di 0x80
nel primo sotto -espressione mascherata dal secondo (27 aprile 1987 da Alan Mycroft).
Ora possiamo XOR il valore da testare ( x
) con una parola che è stata riempita con il valore byte in cui siamo interessati ( n
). Poiché XORing di un valore con se stesso restituisce un byte zero e diverso da zero, possiamo passare il risultato a haszero
.
Questo è spesso usato in un'implementazione tipica strchr
.
(Stephen M Bennet lo ha suggerito il 13 dicembre 2009. Ulteriori dettagli nei ben noti Bit Twiding Hacks ).
PS
this code is broken for any combination of 1111
's next to a 0
L'hack passa il test della forza bruta (solo essere paziente):
#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;
}
Lots of upvotes for an answer which makes the assumption one chararacter=one byte, which is nowadays not the standard any more
Grazie per l'osservazione.
La risposta non era altro che un saggio su codifiche multi-byte / larghezza variabile :-) (in tutta onestà non è la mia area di competenza e non sono sicuro che sia ciò che l'OP stava cercando).
In ogni caso mi sembra che le idee / trucchi di cui sopra possano essere adattate in qualche modo a MBE (specialmente codifiche auto-sincronizzanti ):
- come indicato in Il commento di Johan l'hack può 'facilmente' essere esteso per funzionare con doppi byte o altro (ovviamente non puoi allungarlo troppo);
- una funzione tipica che individua un carattere in una stringa di caratteri multibyte:
- la tecnica sentinella può essere utilizzata con un po 'di lungimiranza.