Boyer-Moore pratique en C#?
Boyer-Moore est probablement l'algorithme de recherche de texte non indexé le plus rapide connu. Je l'implémente donc en C# pour mon site web Black Belt Coder .
Je l'ai fait fonctionner et il a montré à peu près les améliorations de performance attendues par rapport à String.IndexOf()
. Cependant, lorsque j'ai ajouté l'argument StringComparison.Ordinal
à IndexOf
, il a commencé à surpasser mon implémentation Boyer-Moore. Parfois, par une quantité considérable.
Je me demande si quelqu'un peut m'aider à comprendre pourquoi. Je comprends pourquoi StringComparision.Ordinal
pourrait accélérer les choses, mais comment pourrait - il être plus rapide que Boyer-Moore? Est-ce à cause de la surcharge de la plate-forme.net elle-même, peut-être parce que les index de tableau doivent être validés pour s'assurer qu'ils sont à portée, ou autre chose. Certains algorithmes ne sont-ils pas pratiques dans C#.NET?
Ci-dessous est le code clé.
// Base for search classes
abstract class SearchBase
{
public const int InvalidIndex = -1;
protected string _pattern;
public SearchBase(string pattern) { _pattern = pattern; }
public abstract int Search(string text, int startIndex);
public int Search(string text) { return Search(text, 0); }
}
/// <summary>
/// A simplified Boyer-Moore implementation.
///
/// Note: Uses a single skip array, which uses more memory than needed and
/// may not be large enough. Will be replaced with multi-stage table.
/// </summary>
class BoyerMoore2 : SearchBase
{
private byte[] _skipArray;
public BoyerMoore2(string pattern)
: base(pattern)
{
// TODO: To be replaced with multi-stage table
_skipArray = new byte[0x10000];
for (int i = 0; i < _skipArray.Length; i++)
_skipArray[i] = (byte)_pattern.Length;
for (int i = 0; i < _pattern.Length - 1; i++)
_skipArray[_pattern[i]] = (byte)(_pattern.Length - i - 1);
}
public override int Search(string text, int startIndex)
{
int i = startIndex;
// Loop while there's still room for search term
while (i <= (text.Length - _pattern.Length))
{
// Look if we have a match at this position
int j = _pattern.Length - 1;
while (j >= 0 && _pattern[j] == text[i + j])
j--;
if (j < 0)
{
// Match found
return i;
}
// Advance to next comparision
i += Math.Max(_skipArray[text[i + j]] - _pattern.Length + 1 + j, 1);
}
// No match found
return InvalidIndex;
}
}
EDIT: j'ai posté tout mon code de test et mes conclusions sur le sujet à http://www.blackbeltcoder.com/Articles/algorithms/fast-text-search-with-boyer-moore.
2 réponses
Sur la base de mes propres tests et des commentaires faits ici, j'ai conclu que la raison pour laquelle String.IndexOf()
fonctionne si bien avec StringComparision.Ordinal
est que la méthode appelle du code non géré qui utilise probablement un langage d'assemblage optimisé à la main.
J'ai exécuté un certain nombre de tests différents et String.IndexOf()
semble juste être plus rapide que tout ce que je peux implémenter en utilisant du code C# géré.
Si quelqu'un est intéressé, j'ai écrit tout ce que j'ai découvert à ce sujet et posté plusieurs variantes du Boyer-Moore algorithme en C# à http://www.blackbeltcoder.com/Articles/algorithms/fast-text-search-with-boyer-moore.
Mon pari est que le réglage de ce drapeau autorise la chaîne.IndexOf pour utiliser Boyer-Moore lui-même. Et sa mise en œuvre est meilleure que la vôtre.
Sans ce drapeau, il doit être prudent en utilisant Boyer-Moore (et probablement pas) en raison de problèmes potentiels autour D'Unicode. En particulier, la possibilité D'Unicode fait exploser les tables de transition que Boyer-Moore utilise.