Я ответил на этот вопрос ранее, и Эллиот указал, что я был просто неправ.Я извиняюсь перед сообществом.
В коде String.indexOf нет ничего волшебного.Это не изначально оптимизировано или что-то в этом роде.Вы можете скопировать метод indexOf из исходного кода String, и он будет запущен так же быстро.
Здесь мы видим разницу между эффективностью O () и фактической эффективностью.Рабин-Карп для строки длины N и шаблона длины M, Рабин-Карп равен O (N + M) и наихудший случай O (NM).Когда вы смотрите на это, String.indexOf () также имеет лучший регистр O (N + M) и худший регистр O (NM).
Если текст содержит много частичных совпадений с началомшаблон Рабина-Карпа останется близким к наилучшей производительности, в то время как String.indexOf - нет.Например, я тестировал приведенный выше код (на этот раз правильно :-)) на миллион '0', за которым следует один '1', а поиск 1000 '0' сопровождается одним '1'.Это вынудило String.indexOf работать в худшем случае.Для этого сильно вырожденного теста алгоритм Рабина-Карпа был примерно в 15 раз быстрее, чем indexOf.
Для текста на естественном языке Rabin-Karp останется близким к лучшему, а indexOf будет только слегка ухудшаться.Следовательно, решающим фактором является сложность операций, выполняемых на каждом шаге.
В самом внутреннем цикле indexOf сканирует соответствующий первый символ.На каждой итерации приходится:
- увеличить счетчик цикла
- выполнить два логических теста
- сделать один доступ к массиву
ВРабин-Карп на каждой итерации должен:
- увеличить счетчик цикла
- выполнить два логических теста
- сделать два доступа к массиву (фактически два вызова метода)
- обновить хеш, для чего требуется 9 числовых операций
Поэтому на каждой итерации Рабин-Карп будет отставать все дальше и дальше.Я попытался упростить алгоритм хеширования до просто символов XOR, но у меня все еще был дополнительный доступ к массиву и две дополнительные числовые операции, поэтому он был еще медленнее.
Кроме того, когда совпадение найдено, Рабин-Карп знает толькосовпадения хэшей и, следовательно, должны проверять каждый символ, в то время как indexOf уже знает совпадения с первым символом и, следовательно, имеет на один тест меньше.
Читая в Википедии, что Рабин-Карп используется для обнаружения плагиата, я взял БиблиюBook of Ruth убрал все знаки препинания и сделал все строчными, оставив чуть менее 10000 символов.Затем я искал «andthewomenherneighboursgaveitaname», который встречается в самом конце текста.String.indexOf был еще быстрее, даже с хэшем XOR.Однако если я исключил преимущество String.indexOfs, связанное с возможностью доступа к частному внутреннему массиву символов String, и заставил его скопировать массив символов, то, наконец, Рабин-Карп был действительно быстрее.
Однако я сознательно выбралэтот текст, как есть 213 "и" в Книге Рут и 28 "и".Если вместо этого я искал только последние символы «ursgaveitaname», то в тексте всего 3 «urs», поэтому indexOf возвращается ближе к своему лучшему случаю и снова выигрывает гонку.
В качестве более справедливого тестаЯ выбрал случайные 20 строк символов из второй половины текста и рассчитал их время.Рабин-Карп был примерно на 20% медленнее, чем алгоритм indexOf, работающий вне класса String, и на 70% медленнее, чем фактический алгоритм indexOf.Таким образом, даже в случае использования, для которого он предположительно подходит, он все еще был не лучшим выбором.
Так что же хорошего в Рабин-Карп?Независимо от длины или характера текста, который нужно найти, для каждого сравниваемого символа он будет медленнее.Независимо от того, какую хеш-функцию мы выберем, мы обязательно должны сделать дополнительный доступ к массиву и по крайней мере две числовые операции.Более сложная хеш-функция даст нам меньше ложных совпадений, но потребует больше числовых операторов.Рабин-Карп просто не может не отставать.
Как показано выше, если нам нужно найти совпадение с префиксом часто повторяющегося блока текста, indexOf может быть медленнее, но если мы знаем, что мы делаем это, это будет выглядеть так, как будто нам все-таки лучше использовать indexOf для поиска для текста без префикса, а затем проверьте, чтобы увидеть, присутствовал ли префикс.
Судя по моим сегодняшним исследованиям, я не вижу времени, когда дополнительная сложность Рабина Карпа окупится.