Có chương trình nào phù hợp với chuỗi mờ, cung cấp điểm số phù hợp không?


17

Tôi có danh sách các chuỗi trong tập tin Avà tập tin B. Tôi muốn lấy từng chuỗi trong tệp A và tìm chuỗi tương tự nhất trong tệp B.

Đối với điều này, tôi đang tìm kiếm một công cụ cung cấp so sánh mờ.

ví dụ:

$ fuzzy_compare "Some string" "Some string"
100

Trong đó 100 là một số tỷ lệ bình đẳng. Ví dụ khoảng cách Levenshtein .

Có tiện ích nào không? Tôi không muốn phát minh lại bánh xe.


1
Tôi đã chỉnh sửa câu hỏi của bạn để cải thiện sự rõ ràng nhưng đã thay đổi nó để hỏi về việc so sánh từng chuỗi trong tệpA với các chuỗi trong tệpB và không chỉ chuỗi đầu tiên. Tôi cho rằng đó là những gì bạn muốn nói nhưng xin hãy sửa tôi nếu tôi sai.
terdon


@muru không, đó chỉ là kết hợp mờ, OP cần điểm số.
terdon

Câu trả lời:


23

Tôi tìm thấy trang này cung cấp các triển khai thuật toán khoảng cách Levenshtein bằng các ngôn ngữ khác nhau. Vì vậy, ví dụ trong bash, bạn có thể làm:

#!/bin/bash
function levenshtein {
    if [ "$#" -ne "2" ]; then
        echo "Usage: $0 word1 word2" >&2
    elif [ "${#1}" -lt "${#2}" ]; then
        levenshtein "$2" "$1"
    else
        local str1len=$((${#1}))
        local str2len=$((${#2}))
        local d i j
        for i in $(seq 0 $(((str1len+1)*(str2len+1)))); do
            d[i]=0
        done
        for i in $(seq 0 $((str1len))); do
            d[$((i+0*str1len))]=$i
        done
        for j in $(seq 0 $((str2len))); do
            d[$((0+j*(str1len+1)))]=$j
        done

        for j in $(seq 1 $((str2len))); do
            for i in $(seq 1 $((str1len))); do
                [ "${1:i-1:1}" = "${2:j-1:1}" ] && local cost=0 || local cost=1
                local del=$((d[(i-1)+str1len*j]+1))
                local ins=$((d[i+str1len*(j-1)]+1))
                local alt=$((d[(i-1)+str1len*(j-1)]+cost))
                d[i+str1len*j]=$(echo -e "$del\n$ins\n$alt" | sort -n | head -1)
            done
        done
        echo ${d[str1len+str1len*(str2len)]}
    fi
}

while read str1; do
        while read str2; do
                lev=$(levenshtein "$str1" "$str2");
                printf '%s / %s : %s\n' "$str1" "$str2" "$lev"
        done < "$2"
done < "$1"

Lưu nó dưới dạng ~/bin/levenshtein.sh, làm cho nó có thể thực thi được ( chmod a+x ~/bin/levenshtein.sh) và chạy nó trên hai tệp của bạn. Ví dụ:

$ cat fileA
foo
zoo
bar
fob
baar
$ cat fileB
foo
loo
baar
bob
gaf
$ a.sh fileA fileB
foo / foo : 0
foo / loo : 1
foo / baar : 4
foo / bob : 2
foo / gaf : 3
zoo / foo : 1
zoo / loo : 1
zoo / baar : 4
zoo / bob : 2
zoo / gaf : 3
bar / foo : 3
bar / loo : 3
bar / baar : 1
bar / bob : 2
bar / gaf : 2
fob / foo : 1
fob / loo : 2
fob / baar : 4
fob / bob : 1
fob / gaf : 3
baar / foo : 4
baar / loo : 4
baar / baar : 0
baar / bob : 3
baar / gaf : 3

Điều đó tốt cho một vài mẫu nhưng sẽ rất chậm đối với các tệp lớn hơn. Nếu đó là một vấn đề, hãy thử một trong những triển khai bằng các ngôn ngữ khác. Ví dụ: Perl:

#!/usr/bin/perl 
use List::Util qw(min);

sub levenshtein
{
    my ($str1, $str2) = @_;
    my @ar1 = split //, $str1;
    my @ar2 = split //, $str2;

    my @dist;
    $dist[$_][0] = $_ foreach (0 .. @ar1);
    $dist[0][$_] = $_ foreach (0 .. @ar2);

    foreach my $i (1 .. @ar1) {
        foreach my $j (1 .. @ar2) {
            my $cost = $ar1[$i - 1] eq $ar2[$j - 1] ? 0 : 1;
            $dist[$i][$j] = min(
                            $dist[$i - 1][$j] + 1, 
                            $dist[$i][$j - 1] + 1, 
                            $dist[$i - 1][$j - 1] + $cost
                             );
        }
    }

    return $dist[@ar1][@ar2];
}
open(my $fh1, "$ARGV[0]");
open(my $fh2, "$ARGV[1]");
chomp(my @strings1=<$fh1>);
chomp(my @strings2=<$fh2>);

foreach my $str1 (@strings1) {
    foreach my $str2 (@strings2) {
        my $lev=levenshtein($str1, $str2);
        print "$str1 / $str2 : $lev\n";
    }
}

Như trên, lưu tập lệnh dưới dạng ~/bin/levenshtein.plvà làm cho nó có thể thực thi được và chạy nó với hai tệp làm đối số:

~/bin/levenstein.pl fileA fileB

Ngay cả trong các tệp rất nhỏ được sử dụng ở đây, cách tiếp cận Perl nhanh hơn 10 lần so với bash:

$ time levenshtein.sh fileA fileB > /dev/null

real    0m0.965s
user    0m0.070s
sys     0m0.057s

$ time levenshtein.pl fileA fileB > /dev/null
real    0m0.011s
user    0m0.010s
sys     0m0.000s

Để thêm một số giải thích về kết quả: trích dẫn wikipedia, khoảng cách Levenshtein giữa hai từ là số lần chỉnh sửa ký tự đơn tối thiểu (nghĩa là chèn, xóa hoặc thay thế) cần thiết để thay đổi một từ thành từ khác . Điều đó có nghĩa là số càng thấp, trận đấu càng tốt. Một số không có nghĩa là một kết hợp hoàn hảo. Cũng lưu ý rằng khoảng cách Levenshtein xử lý mỗi nhân vật chỉnh sửa bằng nhau, có nghĩa là "foo" và "Foo" dẫn đến khoảng cách tương tự như "foo" và "cáo".
scai
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.