[c] 가장 빠른 하위 문자열 검색 알고리즘은 무엇입니까?

좋아, 나는 바보처럼 들리지 않는다. 나는 문제 / 요구 사항을보다 명확하게 언급 할 것이다.

  • 바늘 (패턴)과 건초 더미 (검색 할 텍스트)는 모두 C 스타일의 널 종료 문자열입니다. 길이 정보가 제공되지 않습니다. 필요한 경우 계산해야합니다.
  • 함수는 첫 번째 일치 항목에 대한 포인터를 반환해야합니다 NULL.
  • 실패 사례는 허용되지 않습니다. 즉, 일정하지 않은 (또는 큰 상수) 스토리지 요구 사항이있는 알고리즘은 할당 실패에 대한 폴백 사례가 있어야하고 폴백 관리의 성능이 최악의 성능에 기여한다는 것을 의미합니다.
  • 코드가없는 알고리즘 (또는 그러한 링크에 대한 링크)에 대한 좋은 설명도 괜찮지 만 구현은 C로되어 있습니다.

… 그리고 “가장 빠름”의 의미는 다음과 같습니다.

  • 결정 론적 O(n)위치 n= 건초 더미 길이. (하지만 O(nm)결정 론적 O(n)결과 를 제공하기 위해보다 강력한 알고리즘과 결합 된 경우 일반적으로 롤링 해시와 같은 알고리즘의 아이디어를 사용할 수 있습니다 ).
  • if (!needle[1])순진한 무차별 대입 알고리즘보다, 특히 가장 일반적인 경우 일 수있는 매우 짧은 바늘에서 (실제로 몇 개의 시계 등은 괜찮습니다.) 나쁘게 수행하지 마십시오 . (무조건적인 무거운 전처리 오버 헤드는 불량한 바늘을 희생시키면서 병리학 적 바늘의 선형 계수를 개선하려고 시도함에 따라 나쁘다.)
  • 임의의 바늘과 건초 더미가 주어지면 다른 널리 구현되는 알고리즘과 비교할 수 있거나 더 나은 성능 (검색 시간이 50 % 이상 길지 않음)입니다.
  • 이러한 조건을 제외하고는 “가장 빠른”개방형 정의를 그대로 둡니다. 좋은 대답은 왜 당신이 “가장 빠른”제안하는 접근법을 고려하는지 설명해야합니다.

현재 구현은 glibc의 Two-Way 구현보다 약 10 % 느리고 8 배 빠릅니다 (입력에 따라 다름).

업데이트 : 현재 최적의 알고리즘은 다음과 같습니다.

  • 길이가 1 인 바늘에는을 사용하십시오 strchr.
  • 길이가 2-4 인 바늘의 경우 머신 워드를 사용하여 다음과 같이 한 번에 2-4 바이트를 비교하십시오. . 건초 더미의 모든 바이트는 정확히 한 번 읽히고 0 (문자열 끝)과 하나의 16 비트 또는 32 비트 비교에 대한 검사가 수행됩니다.
  • 길이가 4보다 큰 바늘의 경우 창의 마지막 바이트에만 적용되는 잘못된 이동 테이블 (Boyer-Moore와 같은)이있는 양방향 알고리즘을 사용하십시오. 많은 중간 길이의 바늘에 대한 순 손실 인 1kb 테이블을 초기화하는 오버 헤드를 피하기 위해 시프트 테이블의 항목이 초기화되는 비트 배열 (32 바이트)을 표시합니다. 설정되지 않은 비트는 바늘에 나타나지 않는 바이트 값에 해당하며 전체 바늘 길이 이동이 가능합니다.

내 마음에 남아있는 큰 질문은 다음과 같습니다.

  • 잘못된 시프트 테이블을 더 잘 활용할 수있는 방법이 있습니까? Boyer-Moore는 뒤로 (오른쪽에서 왼쪽으로) 스캔하여 최대한 활용할 수 있지만 양방향에서는 왼쪽에서 오른쪽으로 스캔해야합니다.
  • 일반적인 경우에 대해 찾은 두 가지 실행 가능한 후보 알고리즘 (메모리 부족 또는 2 차 성능 조건 없음)은 정렬 된 알파벳에서 양방향문자열 일치입니다 . 그러나 다른 알고리즘이 최적 인 경우를 쉽게 감지 할 수있는 경우가 있습니까? 공간 알고리즘에서 확실히 O(m)( m바늘 길이는) 많은 부분 이 사용될 수 있습니다 m<100. 선형 시간 만 필요로하는 바늘에 대한 쉬운 테스트가있는 경우 최악의 2 차 알고리즘을 사용할 수도 있습니다.

보너스 포인트 :

  • needle과 haystack이 잘 구성된 UTF-8이라고 가정하여 성능을 향상시킬 수 있습니까? (바이트 길이가 다양한 문자를 사용하면 올바른 형식으로 바늘과 건초 더미 사이에 일부 문자열 정렬 요구 사항이 적용되며 불일치하는 헤드 바이트가 발생하면 자동으로 2-4 바이트 이동이 가능합니다. 그러나 이러한 제약 조건으로 인해 최대 접미사 계산, 좋은 접미사 이동 등은 이미 다양한 알고리즘을 제공합니까?)

참고 : 실제로 알고리즘의 성능이 좋지는 않지만 대부분의 알고리즘을 잘 알고 있습니다. 사람들이 나에게 알고리즘에 대한 의견 / 답변으로 계속 언급하지 않도록 좋은 참고 자료가 있습니다. http://www-igm.univ-mlv.fr/~lecroq/string/index.html



답변

가능한 바늘과 건초 더미로 구성된 테스트 라이브러리를 구축하십시오. 무차별 대입을 포함한 여러 검색 알고리즘에서 테스트를 프로파일 링합니다. 데이터에 가장 적합한 것을 선택하십시오.

Boyer-Moore 는 접미사 테이블이 좋은 잘못된 문자 테이블을 사용합니다.

Boyer-Moore-Horspool 은 잘못된 문자표를 사용합니다.

Knuth-Morris-Pratt 는 부분 일치 테이블을 사용합니다.

Rabin-Karp 는 실행중인 해시를 사용합니다.

그것들은 모두 다른 정도의 비교를 위해 오버 헤드를 교환하므로 실제 성능은 바늘과 건초 더미의 평균 길이에 달려 있습니다. 초기 오버 헤드가 많을수록 입력이 길수록 좋습니다. 바늘이 매우 짧으면 무차별적인 힘이 이길 수 있습니다.

편집하다:

기본 쌍, 영어 구 또는 단일 단어를 찾는 데 다른 알고리즘이 가장 좋습니다. 모든 입력에 대해 하나의 최상의 알고리즘이 있다면 공개되었을 것입니다.

다음의 작은 테이블에 대해 생각해보십시오. 각 물음표마다 다른 최상의 검색 알고리즘이있을 수 있습니다.

                 short needle     long needle
short haystack         ?               ?
long haystack          ?               ?

이것은 각 축에서 더 짧거나 더 긴 입력 범위를 갖는 그래프 여야합니다. 이러한 그래프에 각 알고리즘을 플로팅하면 각각 다른 서명을 갖게됩니다. 일부 알고리즘은 패턴에서 많은 반복이 발생하여 유전자 검색과 같은 용도에 영향을 줄 수 있습니다. 전체 성능에 영향을 미치는 다른 요소로는 동일한 패턴을 두 번 이상 검색하고 다른 패턴을 동시에 검색하는 것입니다.

샘플 세트가 필요한 경우 Google 또는 Wikipedia와 같은 사이트를 긁어 내고 모든 결과 페이지에서 html을 제거한다고 생각합니다. 검색 사이트의 경우 단어를 입력 한 다음 제안 된 검색 구 중 하나를 사용하십시오. 해당되는 경우 몇 가지 다른 언어를 선택하십시오. 웹 페이지를 사용하면 모든 텍스트가 중간에서 짧아 지므로 더 긴 텍스트를 얻을 수 있도록 충분한 페이지를 병합하십시오. 공개 도서, 법률 기록 및 기타 큰 본문을 찾을 수도 있습니다. 또는 사전에서 단어를 선택하여 임의의 컨텐츠를 생성하십시오. 그러나 프로파일 링의 요점은 검색 할 컨텐츠 유형에 대해 테스트하는 것이므로 가능한 경우 실제 샘플을 사용하십시오.

나는 짧고 긴 모호함을 남겼습니다. 바늘의 경우 8 자 이하, 중간자 64 자 이하, 1k 자 이하로 짧게 생각합니다. 건초 더미의 경우 2 ^ 10 미만, 중간 2 ^ 20 미만, 최대 2 ^ 30 자 정도로 생각합니다.


답변

2011 년에 출판 된 Dany Breslauer, Roberto Grossi, Filippo Mignosi 의 “Simple Real-Time Constant-Space String Matching” 알고리즘 일 것입니다.

최신 정보:

2014 년 저자는 이러한 개선을 출판 : 최적의 문자열 일치를 포장 향해 .


답변

http://www-igm.univ-mlv.fr/~lecroq/string/index.html는
당신이이 가장 잘 알려진 및 연구 문자열 매칭 알고리즘의 일부의 훌륭한 소스 및 요약 포인트 연결합니다.

대부분의 검색 문제에 대한 솔루션에는 사전 처리 오버 헤드, 시간 및 공간 요구 사항과 관련하여 트레이드 오프가 포함됩니다. 모든 경우에 단일 알고리즘이 최적이거나 실용적이지는 않습니다.

문자열 검색을 위해 특정 알고리즘을 설계하는 것이 목표라면, 나머지 말을 무시하십시오. 일반화 된 문자열 검색 서비스 루틴을 개발하려면 다음을 시도하십시오.

이미 언급 한 알고리즘의 강점과 약점을 검토하는 데 시간을 보내십시오. 관심있는 문자열 검색의 범위와 범위를 포괄하는 일련의 알고리즘을 찾는 것을 목표로 검토를 수행하십시오. 그런 다음 주어진 입력에 가장 적합한 알고리즘을 대상으로하는 분류기 함수를 기반으로 프론트 엔드 검색 선택기를 빌드하십시오. 이런 식으로 가장 효율적인 알고리즘을 사용하여 작업을 수행 할 수 있습니다. 이는 특정 검색에 알고리즘이 매우 우수하지만 성능이 저하 될 때 특히 효과적입니다. 예를 들어, 짐승의 힘은 바늘 길이가 증가함에 따라 길이 1의 바늘 있지만 빨리 저하, 그러자 대한 아마 최고입니다 sustik – 무어 algoritim더 작은 바늘보다 더 효율적일 수 있고, 더 긴 바늘과 더 큰 알파벳의 경우 KMP 또는 Boyer-Moore 알고리즘이 더 나을 수 있습니다. 다음은 가능한 전략을 설명하기위한 예일뿐입니다.

다중 알고리즘은 새로운 아이디어가 아닙니다. 나는 그것이 몇몇 상용 Sort / Search 패키지에 의해 사용되었다고 생각한다 (예를 들어 메인 프레임에서 일반적으로 사용되는 SYNCSORT는 몇 가지 정렬 알고리즘을 구현하고 휴리스틱을 사용하여 주어진 입력에 가장 적합한 것을 선택한다)

각 검색 알고리즘은 예를 들어이 백서에서 설명하는 것처럼 성능에 큰 차이를 만들 수있는 몇 가지 변형이 있습니다 .

서비스를 벤치마킹하여 추가 검색 전략이 필요한 영역을 분류하거나 선택기 기능을보다 효과적으로 조정하십시오. 이 방법은 빠르거나 쉽지는 않지만 잘 수행하면 매우 좋은 결과를 얻을 수 있습니다.


답변

이 토론에서 기술 보고서가 인용 된 것을보고 놀랐습니다. 위의 Sustik-Moore라는 알고리즘의 저자 중 하나입니다. (우리는이 용어를 우리 논문에서 사용하지 않았습니다.)

여기서 알고리즘의 가장 흥미로운 특징은 각 문자가 최대 한 번만 검사된다는 것을 증명하는 것이 매우 간단하다는 점입니다. 이전의 보이어-무어 (Boyer-Moore) 이전 버전에서는 각 문자가 최대 3 회 이상 최대 2 회까지 검사되었으며, 그 증거가 더 많이 관련되어 있음을 증명했습니다 (서류 인용 참조). 그러므로 나는 또한이 변형을 제시 / 연구하는데 교훈적인 가치를 볼 수 있습니다.

이 백서에서는 이론적 보장을 완화하면서 효율성을 향한 추가 변형을 설명합니다. 짧은 논문이며 자료는 제 생각에 보통의 고등학교 졸업생에게 이해할 수 있어야합니다.

우리의 주요 목표는이 버전을 더 향상시킬 수있는 다른 사람들의 관심을 끌기위한 것입니다. 문자열 검색에는 많은 변형이 있으므로이 아이디어가 이점을 가져올 수있는 모든 것을 생각할 수는 없습니다. (고정 텍스트 및 패턴 변경, 고정 패턴 다른 텍스트, 전처리 가능 / 불가능, 병렬 실행, 큰 텍스트에서 일치하는 하위 집합 찾기, 오류 허용, 일치하는 것 등)


답변

가장 빠른 하위 문자열 검색 알고리즘은 컨텍스트에 따라 다릅니다.

  1. 알파벳 크기 (예 : DNA 대 영어)
  2. 바늘 길이

2010 년 논문 “정확한 문자열 일치 문제 : 포괄적 인 실험 평가” 에서는 51 개의 알고리즘 (알파벳 크기와 바늘 길이가 다른)에 대한 런타임이있는 테이블을 제공하므로 상황에 가장 적합한 알고리즘을 선택할 수 있습니다.

이러한 모든 알고리즘에는 C 구현과 테스트 스위트가 있습니다.

http://www.dmi.unict.it/~faro/smart/algorithms.php


답변

정말 좋은 질문입니다. 작은 비트를 추가하십시오 …

  1. 누군가가 DNA 서열 매칭에 대해 이야기하고있었습니다. 그러나 DNA 서열의 경우, 일반적으로 건초 더미에 대한 데이터 구조 (예 : 접미사 배열, 접미사 트리 또는 FM 색인)를 작성하고 많은 바늘을 일치시키는 것입니다. 이것은 다른 질문입니다.

  2. 누군가가 다양한 알고리즘을 벤치마킹하고 싶다면 정말 좋을 것입니다. 압축 및 접미사 배열 구성에 대한 벤치 마크는 매우 좋지만 문자열 일치에 대한 벤치 마크는 보지 못했습니다. 잠재적 건초 더미 후보는 SACA 벤치 마크 에서 올 수 있습니다 .

  3. 며칠 전 권장 페이지 에서 Boyer-Moore 구현을 테스트하고있었습니다 (편집 : memmem ()과 같은 함수 호출이 필요하지만 표준 함수는 아니므로 구현하기로 결정했습니다). 내 벤치마킹 프로그램은 임의 건초 더미를 사용합니다. 이 페이지의 Boyer-Moore 구현은 glibc의 memmem () 및 Mac의 strnstr ()보다 몇 배나 빠릅니다. 관심이 있으시면 구현이 여기 있고 벤치마킹 코드가 여기 있습니다 . 이것은 실제로 현실적인 벤치 마크는 아니지만 시작입니다.


답변

나는 그것이 오래된 질문이라는 것을 알고 있지만, 대부분의 나쁜 교대 테이블은 단일 문자입니다. 데이터 세트에 적합하다면 (예 : 단어를 쓰는 경우), 여유 공간이 있으면 단일 문자가 아닌 n-gram으로 구성된 잘못된 시프트 테이블을 사용하여 속도를 크게 높일 수 있습니다.