Resta inteso che il caso peggiore è che O(N)
ci sono alcune microottimizzazioni molto belle.
Il metodo ingenuo esegue un confronto tra caratteri e un confronto di fine testo per ciascun carattere.
L'uso di una sentinella (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 un byte in una parola ( x
) ha un valore specifico ( n
).
La sottoespressione restituisce v - 0x01010101UL
un bit alto impostato in qualsiasi byte ogni volta che il byte corrispondente in v
è zero o maggiore di 0x80
.
La sottoespressione ~v & 0x80808080UL
valuta i bit alti impostati in byte in cui il byte di v
non ha il bit alto impostato (quindi il byte era inferiore a 0x80
).
Andando su queste due sottoespressioni ( haszero
) il risultato sono i bit alti impostati in cui i byte v
erano zero, poiché i bit alti impostati a causa di un valore maggiore rispetto 0x80
alla prima sottoespressione vengono mascherati dal secondo (27 aprile, 1987 di Alan Mycroft).
Ora possiamo XOR il valore di test ( x
) con una parola che è stata riempita con il valore di byte a cui siamo interessati ( n
). Poiché XORing di un valore con se stesso comporta un byte zero e un valore diverso da zero in caso contrario, possiamo passare il risultato a haszero
.
Questo è spesso usato in strchr
un'implementazione tipica .
(Stephen M Bennet lo ha suggerito il 13 dicembre 2009. Ulteriori dettagli nel noto Bit Twiddling Hacks ).
PS
questo codice è rotto per qualsiasi combinazione di 1111
"a"0
L'hack ha superato il test della forza bruta (basta essere pazienti):
#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;
}
Molti voti positivi per una risposta che rende l'assunzione un carattere caratteristico = un byte, che al giorno d'oggi non è più lo standard
Grazie per l'osservazione.
La risposta doveva essere tutt'altro che un saggio sulle codifiche multi-byte / a larghezza variabile :-) (in tutta onestà non è la mia area di competenza e non sono sicuro che sia ciò che l'OP stava cercando).
Ad ogni modo, mi sembra che le idee / i trucchi di cui sopra possano essere in qualche modo adattati a MBE (in particolare le codifiche auto-sincronizzanti ):
- come osservato nel commento di Johan, l'hack può essere "facilmente" esteso per funzionare con doppi byte o altro (ovviamente non si può allungare 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.