본문 바로가기

NLP ( 자연어 처리 )

Edit distance

Edit distance 를 구하기 위해 java 로 짜보았다.





class EditDistance {

	// ㄱ ㄲ ㄴ ㄷ ㄸ ㄹ ㅁ ㅂ ㅃ ㅅ ㅆ ㅇ ㅈ ㅉ ㅊ ㅋ ㅌ ㅍ ㅎ
	final static char[] ChoSung = { 0x3131, 0x3132, 0x3134, 0x3137, 0x3138, 0x3139, 0x3141, 0x3142, 0x3143, 0x3145,
			0x3146, 0x3147, 0x3148, 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e };
	// ㅏ ㅐ ㅑ ㅒ ㅓ ㅔ ㅕ ㅖ ㅗ ㅘ ㅙ ㅚ ㅛ ㅜ ㅝ ㅞ ㅟ ㅠ ㅡ ㅢ ㅣ
	final static char[] JwungSung = { 0x314f, 0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157, 0x3158,
			0x3159, 0x315a, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f, 0x3160, 0x3161, 0x3162, 0x3163 };
	// ㄱ ㄲ ㄳ ㄴ ㄵ ㄶ ㄷ ㄹ ㄺ ㄻ ㄼ ㄽ ㄾ ㄿ ㅀ ㅁ ㅂ ㅄ ㅅ ㅆ ㅇ ㅈ ㅊ ㅋ ㅌ ㅍ ㅎ
	final static char[] JongSung = { 0, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, 0x3139, 0x313a, 0x313b,
			0x313c, 0x313d, 0x313e, 0x313f, 0x3140, 0x3141, 0x3142, 0x3144, 0x3145, 0x3146, 0x3147, 0x3148, 0x314a,
			0x314b, 0x314c, 0x314d, 0x314e };

	public static void main(String[] args) {

		if (args.length < 2) {
			System.err.println("자소 분리할 단어를 2개 입력하세요.");
			System.exit(1); 
		}

		try {
			String Jaso1 = hangulToJaso(args[0]);
			String Jaso2 = hangulToJaso(args[1]);

			System.out.println("Jaso1 : " + Jaso1 + ", Jaso2 : " + Jaso2);
			System.out.println("distance : " + Integer.toString(minDistance(Jaso1, Jaso2)));
		} catch (Exception e) {
			System.err.println(e); 
			System.exit(1);
		}

	} 
	
	public static String hangulToJaso(String s) { // 유니코드 한글 문자열을 입력 받음
		int a, b, c; // 자소 버퍼: 초성/중성/종성 순
		String result = "";

		for (int i = 0; i < s.length(); i++) {
			char ch = s.charAt(i);

			if (ch >= 0xAC00 && ch <= 0xD7A3) { // "AC00:가" ~ "D7A3:힣" 에 속한 글자면
												// 분해

				c = ch - 0xAC00;
				a = c / (21 * 28);
				c = c % (21 * 28);
				b = c / 28;
				c = c % 28;

				result = result + ChoSung[a] + JwungSung[b];
				if (c != 0)
					result = result + JongSung[c]; // c가 0이 아니면, 즉 받침이 있으면
			} else {
				result = result + ch;
			}
		}
		return result;
	}

	public static int minDistance(String word1, String word2) {
		int len1 = word1.length();
		int len2 = word2.length();

		int[][] dp = new int[len1 + 1][len2 + 1];

		for (int i = 0; i <= len1; i++) {
			dp[i][0] = i;
		}

		for (int j = 0; j <= len2; j++) {
			dp[0][j] = j;
		}

		// iterate though, and check last char
		for (int i = 0; i < len1; i++) {
			char c1 = word1.charAt(i);
			for (int j = 0; j < len2; j++) {
				char c2 = word2.charAt(j);

				if (c1 == c2) {
					dp[i + 1][j + 1] = dp[i][j];
				} else {
					int replace = dp[i][j] + 2;
					int insert = dp[i][j + 1] + 1;
					int delete = dp[i + 1][j] + 1;

					int min = replace > insert ? insert : replace;
					min = delete > min ? min : delete;
					dp[i + 1][j + 1] = min;
				}
			}
		}

		return dp[len1][len2];
	}

}





파라미터로 감나무, 밤나무를 넘기면




감나무 잣나무를 넘기면!






자소 또는 음소( phonemes ) 단위로 쪼개는 코드를 삽입했다. 



음절 단위로 쪼갠 경우 감나무 : 밤나무  의 Edit distance 와 감나무 : 잣나무  의 Edit distance 는 각각 1글자의 substitution. 즉, 2로 같았겠지만 이를 자소 단위로 쪼개어 비교하는 경우 감나무 : 밤나무는 1개의 substitution, 감나무 : 잣나무는 2개의 substitution 이 이뤄져야 하므로 값이 각각 2, 4 로 다르게 나타난다.



'NLP ( 자연어 처리 )' 카테고리의 다른 글

katz-backoff  (0) 2016.04.17
ARPA Language models  (0) 2016.04.16
Zipf's law  (0) 2016.04.08
ngram-count  (0) 2016.04.04
n-gram  (0) 2016.04.04