[출처 : http://kerbtier.ch/2008/12/30/levenshtein-to-slow-how-to-speed-it-up]


you don’t need 40′000 levenshtein calls

one attempt to make it faster is to rule out some strings by comparing lengths first. if you say your maximum distance for two strings to match is 10 then you can discard strings which difference in character count is bigger than 10. it’s already much faster, for the 40′000 and for my usage almost fast enough but the faster the better. in this case i still have to check one strings length against 40′000 other strings length. this can be speed up by putting the sets of the strings with the same length into an array where the index is the length of the sets strings. so i don’t have to compare it against all strings. if l is the length of the string i want to test then i need to test it only against the strings in the sets for the indexes with a length from l-10 to l+10. probably i can rule out with this technique even more strings, by example count the number of words in the strings instead of the number of characters or the number of vowels. these approaches could be combined together and it probably will speed it up another bit. but due to statistics the result would probably be about the same like in the case where i use the total length of the string.
[출처 : http://pro.cug.kr/tag/Levenshtein%20Distance]


URL: http://www.merriampark.com/ld.htm

자주쓰이는 3개의 언어로 구현한 Levenshtein Distance

작성자: Michael Gilleland, Merriam Park Software

이 짧은 에세이를 쓰게 된 것은 Levenshtein distance 알고리즘에 대해서
설명하고 또 그것이 각각의 세가지 프로그래밍 언어에서 어떻게 구현되는가를
보이기 위해서입니다.

Levenshtein Distance이란 무엇인가?
데모
알고리즘
세가지 언어로 구현된 소스코드
레퍼런스
다른 언어로 구현

Levenshtein Distance이란 무엇인가?

Levenshtein Distance(이하 LD)는 두 문자열의 비슷한 정도를 측정하기위해 고안되었습니다.
여기서 원문자열을 (s)로, 대상문자열을 (t) 라고 나타낸다고 하겠습니다. distance란 s를
t로 변형시키기 위해 삭제, 추가, 또는 수정이 필요한 횟수를 뜻합니다. 예를든다면,

* s가 "test"이고 t도 "test"라면, LD(s,t) = 0 이 됩니다. 왜냐하면 문자열들이 이미 동일하여 변환이 필요하지 않기 때문입니다.

* s가 "test"이고 t가 "tent"라면, LD(s,t) = 1 이 됩니다. 왜냐하면 s를 t로 만들기 위해서는 "s"를 "n"으로 한번 수정이 필요하기 때문입니다.

Levenshtein distance는 string 간의 차이가 클수록 위대함을 느낄 수 있습니다.

Levenshtein distance는 러시아 과학자인 Vladimir Levenshtein가 1965년에 고안하여 그렇게 이름지어졌습니다.
Levenshtein 이란 단어가 쓰거나 읽기 힘들기 때문에 종종 edit distance라고도 불립니다.

Levenshtein distance 알고리즘은 다음과 같은 분야에 쓰여집니다:

* 철자 검사
* 음성 인식
* DNA 분석
* 표절여부 검사

데모

아래의 간단한 자바 애플릿으로 두 문자열의 Levenshtein distance를 알아보세요.

원래 문자열
대상 문자열


알고리즘

알고리즘 작동 단계
단계 설명

s의 문자열 길이를 n에 넣는다.
t의 문자열의 길이를 m에 넣는다.
만약 n = 0 이라면, m 을 리턴하고 종료한다.
만약 m = 0 이라면, n 을 리턴하고 종료한다.
0..m 행과, 0..n 열로 이루어진 행열을 만든다.
2
첫번째 행인 0..n을 초기화 한다.
첫번째 열인 0..m을 초기화 한다.
3
s의 각 문자(i는 1부터 n까지)를 검사한다.
4
t의 각 문자(j는 1부터 m까지)를 검사한다.
5
s[i]와 t[j]가 같다면, 변경하기 위한 비용은 0이 된다.
s[i]와 t[j]가 같지 않다면, 비용은 1이 된다.
6
행열의 셀 d[i,j]에 다음의 것들 중 가장 작은 값을 넣는다.
a. 바로 위의 셀이 더하기 1이 되는 경우: d[i-1, j] + 1
b. 바로 왼쪽 셀이 더하기 일이 되는 경우: d[i,j-1] + 1
c. 대각선으로 연속적인, 바로 왼,위쪽 셀의 비용: d[i-1,j-1] + cost
7
(3, 4, 5, 6) 단계를 반복하여 완료되면, d[n, m]셀에 있는 것이 distance가 된다.

예제

이 예제절에서는 원래 문자열이 "GUMBO"이고 대상 문자열이 "GAMBOL"이라 할 때
어떻게 Levenshtein distance가 계산되는지에 대해서 다룬다.

1 과 2 단계

i가 1일 때 3에서 6 단계

i가 2일 때 3에서 6 단계 

i가 3일 때 3에서 6 단계

i가 4일 때 3에서 6 단계

i가 5일 때 3에서 6 단계

7단계
행열의 가장 오른쪽 아래에 있는 값이 distance가 된다.(여기서는 2)
이 결과는 "GUMBO"가 "GAMBOL"이 되기 위해서 "U"를 "A"로 바꾸고
"L"을 추가해야한다는, 직관적으로 알 수 있는 결과와 일치합니다.
( 1번의 수정과 1번의 추가 = 2 번의 변경 )

세가지 언어로 구현된 소스코드

프로그래밍 언어들간에 차이에 대해서 토론하는 엔지니어들 사이에서는 종교 전쟁이 일어나기도합니다.
이러한 예로, 전형적인 주장은 JavaWorld article에서 일어난(July 1999) Allen Holub의 주장입니다.:
"예를들자면, 비주얼 베이식은 전혀 객체지향이라고 말할 수 없다. Microsoft Foundation Classes(MFC) 
또는 대부분의 다른 마이크로소프트의 테크놀러지는 어느것도 객체지향이라 주장할 수 없다."

Salon에 계제된(Jan. 8, 2001) Simson Garfinkels의 글에서 다른 진영의 반박이 이루어졌습니다.
이 글은 "Java: 느리고, 꼴사납고, 부적절한 언어"라는 제목으로 알려져 있는데, 명료하게
표현하자면 "나는 자바를 증오해"라고 나타낼 수 있습니다.

우리는 이러한 종교 전쟁들 속에서 자연스럽고 조심스런 입장을 취하로 했습니다. 배우기 위한 교재로써,
하나의 프로그래밍 언어에서만 해결할 수 있는 문제라면 대개 다른 언어에서도 마찬가지로 해결할 수
있을 것입니다. 우수한 프로그래머는 완전히 새로운 언어를 배우면서 한다고 하더라도 하나의 언어에서 
다른 언어로 비교적 쉽게, 큰 어려움에 당면하지 않고 옮길 수 있습니다. 프로그래밍 언어라는 것은
목적을 이루기 위한 것이지, 그 자체가 목적은 아닌 것입니다.

이러한 중도의 입장에서, 우리는 Levenshtein distance 알고리즘을 아래에 있는 프로그래밍 언어들로 
구현하여 소스코드를 보였습니다.

* Java
* C++
* Visual Basic

소스코드들 (블라블라)

참고문헌

Levenshtein distance에 관련된 다릍 토의를 다음 링크들에서 발견하실 수 있습니다.

* http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Dynamic/Edit.html (Lloyd Allison) 
* http://www.cut-the-knot.com/do_you_know/Strings.html (Alex Bogomolny) 
* http://www-igm.univ-mlv.fr/~lecroq/seqcomp/node2.html (Thierry Lecroq) 

다른 언어로 구현

아래 있는 분들은 그들 각자가 다양한 언어로 Levenshtein Distance 알고리즘을 구현한 것을
여기서 사용할 수 있게 친절히 승낙해 주셨습니다.

* Eli Bendersky 은 펄로 구현해주셨습니다.
* Barbara Boehmer 은 Oracle PL/SQL 로 구현해주셨습니다.
* Rick Bourner Objective-C 로 구현해주셨습니다.
* Joseph Gama 는 TSQL로 Planet Source Code 에 있는 TSQL 함수 패키지의 한 파트로 구현해주셨습니다.
* Anders Sewerin Johansen 는 C++로 제가 만든 것보다 C++의 정신에 가깝게, 더 최적화되고 세련되게 구현해주셨습니다.
* Lasse Johansen 는 C#으로 구현해 주셨습니다.
* Alvaro Jeria Madariaga는 Delphi로 구현해 주셨습니다.
* Lorenzo Seidenari 는 C로 구현해 주셨습니다.
* Steve Southwell는 Progress 4gl로 구현해 주셨습니다.

이 페이지 밖에 있는 다른 구현들:

* Art Taylor의 Emacs Lisp로의구현.
* Magnus Lie Hetland의 Python로의 구현.
* Richard Suchenwirth의 Tcl로의 구현(링크가 깨진 것을 알려주신 Stefan Seidler님 감사합니다).
[출처 : http://markos.gaivo.net/blog/?p=211]


Simon proposes using a dictionary to match mistyped URLs with real ones. I’d probably like the idea better if I actually understood it. Still using Levenshtein can be a bit easier than using a spell checker responsively.

Easier, but slow. I used existing implementation by Magnus Lie Hetland and made a test with 245 blog titles using a simple script. 100 iterations on aging powerbook produced:

1.766s, 29.152s, 9.399s (min, max, avg)

Average time to calculate distance between randomly chosen title and rest of them is 9.4 seconds, which is far too much to be useful. And there’s not even 250 of them.

There are two obvious ways to speed things up. Since minimum distance is at least as much as a difference in length of both strings, there’s no point in calculating it when difference already exceeds a limit we chose.

The other trick took into an account that Levenshtein’s algorithm of two strings of comparable length has complexity of O(n2) and that my blog titles are form quite sparse space. If strings are longer than a certain limit (I arbitrarily chose 10 letters), then first calculate edit distance on a small sparse substring and then calculate the real distance only if first one was close enough.

When I made 1000 iterations of randomly chosen title using only first test, I got following results:

0.003s, 0.284s, 0.167s (min, max, avg)

However, if I used both checks, the same 1000 iteration test got me:

0.003s, 0.162s, 0.027s (min, max, avg)

So, two simple checks can speed up search up to 350 times. Not bad, but I’m not happy. This is certainly fast enough for a personal website or sites where site structure effectively divides searching to relatively small sets of possible hits, but there are plenty of sites where it would be too slow.

[출처 : http://inthechaos.tistory.com/entry/탐색-SearchMainc]




#pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h>
#include "Search.h"
//상수 정의
enum { ARRAY_SIZE = 100, MAX_VALUE = 1000 };
void fillArray(int *array, int size);
void printArray(const int *array, int size);
int compare(const void *p1, const void *p2);
void main(void)
{
 int array[ARRAY_SIZE];
 int value, pos;
 fillArray(array, ARRAY_SIZE);
 printArray(array, ARRAY_SIZE);
 //순차 탐색
 printf("[순차 탐색]\n");
 while(1)
 {
  printf("입력 : ");
  scanf("%d", &value);
  if(value < 0 || value >= MAX_VALUE)
  {
   break;
  }
  pos = linearSearch(array, ARRAY_SIZE, value);
  printf("결과 : %s\n\n", (pos != -1) ? "성공" : "실패");
 }
 printf("\n\n");
 //정렬
 qsort(array, ARRAY_SIZE, sizeof(array[0]), compare);
 printArray(array, ARRAY_SIZE);
 //이진 탐색
 printf("[이진 탐색]\n");
 while(1)
 {
  printf("입력 : ");
  scanf("%d", &value);
  if(value < 0 || value >= MAX_VALUE)
  {
   break;
  }
  pos = linearSearchSort(array, ARRAY_SIZE, value);
  printf("순차 : %s\n\n", (pos != -1) ? "성공" : "실패");
  pos = binarySearch(array, ARRAY_SIZE, value);
  printf("이진 : %s\n\n", (pos != -1) ? "성공" : "실패");
 }
}
void fillArray(int *array, int size)
{
 int i;
 for(i=0; i<size; i++)
 {
  array[i] = rand() % MAX_VALUE;
 }
}
void printArray(const int *array, int size)
{
 int i;
 for(i=0; i<size; i++)
 {
  printf("%3d ", array[i]);
  if(i%10 == 9)
  {
   printf("\n");
  }
 }
 printf("\n\n");
}
int compare(const void *p1, const void *p2)
{
 return *(int*) p1 - *(int*) p2;
}

[출처 : http://inthechaos.tistory.com/entry/탐색-Searchh-Searchc]


Search.h

//선언
int linearSearch(int *array, int size, int value);
int linearSearchSort(int *array, int size, int value);
int binarySearch(int *array, int size, int value);





Search.c

#pragma warning(disable:4996)
#include <stdio.h>
int linearSearch(int *array, int size, int value) { int i; for(i=0; i<size; i++) { if(array[i] == value) { printf("횟수 : 성공[%d번]\n", i); return i; } } printf("횟수 : 실패[%d번]\n", i); return -1; } int linearSearchSort(int *array, int size, int value) { int i; for(i=0; i<size; i++) { if(array[i] >= value) { if(array[i] == value) { printf("횟수 : 성공[%d번]\n", i); return i; } else { break; } } } printf("횟수 : 실패[%d번]\n", i); return -1; } int binarySearch(int *array, int size, int value) { int i, left, right, middle; left = 0, right = size-1; for(i=0; left<=right; i++) { middle = (left+right) / 2; if(array[middle] == value) { printf("횟수 : 성공[%d번]\n", i); return middle; } if(array[middle] < value) { left = middle+1; } else { right = middle-1; } } printf("횟수 : 실패[%d번]\n", i); return -1; }

[출처 : http://inthechaos.tistory.com/entry/정렬-qsort]



 

#pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h>
void printArray(int *array, int size);
int cmpAsc(const void *p1, const void *p2);
int cmpDsc(const void *p1, const void *p2);
void main(void)
{
 int array[11] = { 87, 41, 36, 71, 98, 56, 28, 45, 50, 11, 63 };
 
 printf("원본 : ");
 printArray(array, 11);
 qsort(array, 11, sizeof(array[0]), cmpAsc);
 printf("오름 : ");
 printArray(array, 11);
 qsort(array, 11, sizeof(array[0]), cmpDsc);
 printf("내림 : ");
 printArray(array, 11);
}
void printArray(int *array, int size)
{
 int i;
 for(i=0; i<size; i++)
 {
  printf("%d ", array[i]);
 }
 printf("\n");
}
int cmpAsc(const void *p1, const void *p2)
{
 return *(int*)p1 - *(int*)p2;
}
int cmpDsc(const void *p1, const void *p2)
{
 return cmpAsc(p2, p1);
}

[출처 : http://inthechaos.tistory.com/entry/정렬-SortMainc]

#pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "Sort.h"

void fillArray(int *array, int size); void printArray(const int *array, int size); void bubbleTest(const int *array, int size); void selectionTest(const int *array, int size); void insertionTest(const int *array, int size); void heapTest(const int *array, int size); void mergeTest(const int *array, int size); void quickTest(const int *array, int size); void main(void) { int *array=NULL; int i, size; srand((unsigned) time(NULL)); //초기화 size = 4096; array = malloc(size*128*sizeof(int)); //저속 정렬 printf("[저속 정렬]\n"); for(i=0; i<4; i++) { fillArray(array, size); printf("개수 : %d\n", size); bubbleTest(array, size); selectionTest(array, size); insertionTest(array, size); printf("\n"); size *= 2; } //고속 정렬 printf("\n[고속 정렬]\n"); for(i=0; i<4; i++) { fillArray(array, size); printf("개수 : %d\n", size); heapTest(array, size); mergeTest(array, size); quickTest(array, size); printf("\n"); size *= 2; } free(array); } void fillArray(int *array, int size) { int i; for(i=0; i<size; i++) { array[i] = rand() * rand(); } } void printArray(const int *array, int size) { int i; for(i=0; i<size; i++) { printf("%10d ", array[i]); if(i%7 == 6) { printf("\n"); } } printf("\n"); } void bubbleTest(const int *array, int size) { clock_t start, end; int *temp; temp = malloc(size*sizeof(int)); memcpy(temp, array, size*sizeof(int)); start = clock(); bubbleSort(temp, size); end = clock(); printf("버블 : %.3f\n", (end-start) / (double) CLOCKS_PER_SEC); free(temp); } void selectionTest(const int *array, int size) { clock_t start, end; int *temp; temp = malloc(size*sizeof(int)); memcpy(temp, array, size*sizeof(int)); start = clock(); selectionSort(temp, size); end = clock(); printf("선택 : %.3f\n", (end-start) / (double) CLOCKS_PER_SEC); free(temp); } void insertionTest(const int *array, int size) { clock_t start, end; int *temp; temp = malloc(size*sizeof(int)); memcpy(temp, array, size*sizeof(int)); start = clock(); insertionSort(temp, size); end = clock(); printf("삽입 : %.3f\n", (end-start) / (double) CLOCKS_PER_SEC); free(temp); } void heapTest(const int *array, int size) { clock_t start, end; int *temp; temp = malloc(size*sizeof(int)); memcpy(temp, array, size*sizeof(int)); start = clock(); heapSort(temp, size); end = clock(); printf("힙 : %.3f\n", (end-start) / (double) CLOCKS_PER_SEC); free(temp); } void mergeTest(const int *array, int size) { clock_t start, end; int *temp; temp = malloc(size*sizeof(int)); memcpy(temp, array, size*sizeof(int)); start = clock(); mergeSort(temp, size); end = clock(); printf("합병 : %.3f\n", (end-start) / (double) CLOCKS_PER_SEC); free(temp); } void quickTest(const int *array, int size) { clock_t start, end; int *temp; temp = malloc(size*sizeof(int)+4); temp[size] = INT_MAX; memcpy(temp, array, size*sizeof(int)); start = clock(); quickSort(temp, size); end = clock(); printf("퀵 : %.3f\n", (end-start) / (double) CLOCKS_PER_SEC); free(temp); }
[출처 : http://inthechaos.tistory.com/entry/정렬-Sortc]


#include <stdio.h> #include <malloc.h> #include "Sort.h"

void swap(int *p1, int *p2) { int tmp; tmp = *p1; *p1 = *p2; *p2 = tmp; } //버블 정렬 void bubbleSort(int *array, int size) { int i; for(i=size; i>1; i--) { bubbleUp(array, i); } } void bubbleUp(int *array, int size) { int i; for(i=1; i<size; i++) { if(array[i-1] > array[i]) { swap(array+i-1, array+i); } } } //선택 정렬 void selectionSort(int *array, int size) { int i; for(i=size; i>1; i--) { selection(array, i); } } void selection(int *array, int size) { int i, max=0; for(i=1; i<size; i++) { if(array[i] > array[max]) { max = i; } } swap(array+max, array+size-1); } //삽입 정렬 void insertionSort(int *array, int size) { int i; for(i=2; i<=size; i++) { insertion(array, i); } } void insertion(int *array, int size) { int i, unsorted = array[size-1]; for(i=size-2; i>=0; i--) { if(unsorted >= array[i]) { break; } array[i+1] = array[i]; } array[i+1] = unsorted; } //힙 정렬 void heapSort(int *array, int size) { int i; for(i=size/2; i>=0; i--) { reheapDown(array, size, i); } for(i=size-1; i>0; i--) { swap(array, array+i); reheapDown(array, i, 0); } } void reheapDown(int *array, int size, int parent) { int left, right, max; while(1) { left = parent*2+1; right = parent*2+2; if(left >= size) { break; } if(left == size-1) { max = left; } else if(array[left] > array[right]) { max = left; } else { max = right; } if(array[parent] >= array[max]) { break; } swap(array+parent, array+max); parent = max; } } //합병 정렬 void mergeSort(int *array, int size) { if(size > 1) { mergeSort(array, size/2); mergeSort(array+size/2, size-size/2); merge(array, size/2, size); } } void merge(int *array, int middle, int size) { int i, left, right; int *temp = malloc(size*sizeof(int)); left = 0; right = middle; for(i=0; left<middle && right<size; i++) { if(array[left] <= array[right]) { temp[i] = array[left++]; } else { temp[i] = array[right++]; } } while(left < middle) { temp[i++] = array[left++]; } while(right < size) { temp[i++] = array[right++]; } for(i=0; i<size; i++) { array[i] = temp[i]; } free(temp); } //빠른 정렬 void quickSort(int *array, int size) { if(size > 1) { int pivot = split(array, size); quickSort(array, pivot); quickSort(array+pivot+1, size-pivot-1); } } int split(int *array, int size) { int pivot=array[0], left=1, right=size-1; while(1) { while(array[left] < pivot) { left++; } while(array[right] > pivot) { right--; } if(left >= right) { break; } swap(array+left, array+right); left++, right--; } swap(array, array+right); return right; }

+ Recent posts