Khoảng cách Levenshtein trong T-SQL


Câu trả lời:


100

Tôi đã triển khai chức năng chỉnh sửa khoảng cách Levenshtein tiêu chuẩn trong TSQL với một số tối ưu hóa giúp cải thiện tốc độ so với các phiên bản khác mà tôi biết. Trong trường hợp hai chuỗi có ký tự chung ở đầu (tiền tố dùng chung), ký tự chung ở cuối (hậu tố dùng chung) và khi chuỗi lớn và cung cấp khoảng cách chỉnh sửa tối đa, thì tốc độ cải thiện đáng kể. Ví dụ: khi đầu vào là hai chuỗi ký tự 4000 rất giống nhau và khoảng cách chỉnh sửa tối đa là 2 được chỉ định, thì điều này nhanh hơn gần ba bậc về độ lớn so vớiedit_distance_withinhoạt động trong câu trả lời được chấp nhận, trả về câu trả lời trong 0,073 giây (73 mili giây) so với 55 giây. Nó cũng hiệu quả về bộ nhớ, sử dụng không gian bằng lớn hơn của hai chuỗi đầu vào cộng với một số không gian không đổi. Nó sử dụng một "mảng" nvarchar đại diện cho một cột và thực hiện tất cả các tính toán tại chỗ trong đó, cộng với một số biến int trợ giúp.

Tối ưu hóa:

  • bỏ qua quá trình xử lý tiền tố và / hoặc hậu tố được chia sẻ
  • trả về sớm nếu chuỗi lớn hơn bắt đầu hoặc kết thúc bằng toàn bộ chuỗi nhỏ hơn
  • trả lại sớm nếu sự khác biệt về kích thước đảm bảo sẽ vượt quá khoảng cách tối đa
  • chỉ sử dụng một mảng duy nhất đại diện cho một cột trong ma trận (được triển khai dưới dạng nvarchar)
  • khi khoảng cách tối đa được đưa ra, độ phức tạp thời gian sẽ từ (len1 * len2) đến (min (len1, len2)) tức là tuyến tính
  • khi một khoảng cách tối đa được đưa ra, quay trở lại sớm ngay khi giới hạn khoảng cách tối đa được biết là không thể đạt được

Đây là mã (được cập nhật 20/01/2014 để tăng tốc hơn một chút):

-- =============================================
-- Computes and returns the Levenshtein edit distance between two strings, i.e. the
-- number of insertion, deletion, and sustitution edits required to transform one
-- string to the other, or NULL if @max is exceeded. Comparisons use the case-
-- sensitivity configured in SQL Server (case-insensitive by default).
-- 
-- Based on Sten Hjelmqvist's "Fast, memory efficient" algorithm, described
-- at http://www.codeproject.com/Articles/13525/Fast-memory-efficient-Levenshtein-algorithm,
-- with some additional optimizations.
-- =============================================
CREATE FUNCTION [dbo].[Levenshtein](
    @s nvarchar(4000)
  , @t nvarchar(4000)
  , @max int
)
RETURNS int
WITH SCHEMABINDING
AS
BEGIN
    DECLARE @distance int = 0 -- return variable
          , @v0 nvarchar(4000)-- running scratchpad for storing computed distances
          , @start int = 1      -- index (1 based) of first non-matching character between the two string
          , @i int, @j int      -- loop counters: i for s string and j for t string
          , @diag int          -- distance in cell diagonally above and left if we were using an m by n matrix
          , @left int          -- distance in cell to the left if we were using an m by n matrix
          , @sChar nchar      -- character at index i from s string
          , @thisJ int          -- temporary storage of @j to allow SELECT combining
          , @jOffset int      -- offset used to calculate starting value for j loop
          , @jEnd int          -- ending value for j loop (stopping point for processing a column)
          -- get input string lengths including any trailing spaces (which SQL Server would otherwise ignore)
          , @sLen int = datalength(@s) / datalength(left(left(@s, 1) + '.', 1))    -- length of smaller string
          , @tLen int = datalength(@t) / datalength(left(left(@t, 1) + '.', 1))    -- length of larger string
          , @lenDiff int      -- difference in length between the two strings
    -- if strings of different lengths, ensure shorter string is in s. This can result in a little
    -- faster speed by spending more time spinning just the inner loop during the main processing.
    IF (@sLen > @tLen) BEGIN
        SELECT @v0 = @s, @i = @sLen -- temporarily use v0 for swap
        SELECT @s = @t, @sLen = @tLen
        SELECT @t = @v0, @tLen = @i
    END
    SELECT @max = ISNULL(@max, @tLen)
         , @lenDiff = @tLen - @sLen
    IF @lenDiff > @max RETURN NULL

    -- suffix common to both strings can be ignored
    WHILE(@sLen > 0 AND SUBSTRING(@s, @sLen, 1) = SUBSTRING(@t, @tLen, 1))
        SELECT @sLen = @sLen - 1, @tLen = @tLen - 1

    IF (@sLen = 0) RETURN @tLen

    -- prefix common to both strings can be ignored
    WHILE (@start < @sLen AND SUBSTRING(@s, @start, 1) = SUBSTRING(@t, @start, 1)) 
        SELECT @start = @start + 1
    IF (@start > 1) BEGIN
        SELECT @sLen = @sLen - (@start - 1)
             , @tLen = @tLen - (@start - 1)

        -- if all of shorter string matches prefix and/or suffix of longer string, then
        -- edit distance is just the delete of additional characters present in longer string
        IF (@sLen <= 0) RETURN @tLen

        SELECT @s = SUBSTRING(@s, @start, @sLen)
             , @t = SUBSTRING(@t, @start, @tLen)
    END

    -- initialize v0 array of distances
    SELECT @v0 = '', @j = 1
    WHILE (@j <= @tLen) BEGIN
        SELECT @v0 = @v0 + NCHAR(CASE WHEN @j > @max THEN @max ELSE @j END)
        SELECT @j = @j + 1
    END

    SELECT @jOffset = @max - @lenDiff
         , @i = 1
    WHILE (@i <= @sLen) BEGIN
        SELECT @distance = @i
             , @diag = @i - 1
             , @sChar = SUBSTRING(@s, @i, 1)
             -- no need to look beyond window of upper left diagonal (@i) + @max cells
             -- and the lower right diagonal (@i - @lenDiff) - @max cells
             , @j = CASE WHEN @i <= @jOffset THEN 1 ELSE @i - @jOffset END
             , @jEnd = CASE WHEN @i + @max >= @tLen THEN @tLen ELSE @i + @max END
        WHILE (@j <= @jEnd) BEGIN
            -- at this point, @distance holds the previous value (the cell above if we were using an m by n matrix)
            SELECT @left = UNICODE(SUBSTRING(@v0, @j, 1))
                 , @thisJ = @j
            SELECT @distance = 
                CASE WHEN (@sChar = SUBSTRING(@t, @j, 1)) THEN @diag                    --match, no change
                     ELSE 1 + CASE WHEN @diag < @left AND @diag < @distance THEN @diag    --substitution
                                   WHEN @left < @distance THEN @left                    -- insertion
                                   ELSE @distance                                        -- deletion
                                END    END
            SELECT @v0 = STUFF(@v0, @thisJ, 1, NCHAR(@distance))
                 , @diag = @left
                 , @j = case when (@distance > @max) AND (@thisJ = @i + @lenDiff) then @jEnd + 2 else @thisJ + 1 end
        END
        SELECT @i = CASE WHEN @j > @jEnd + 1 THEN @sLen + 1 ELSE @i + 1 END
    END
    RETURN CASE WHEN @distance <= @max THEN @distance ELSE NULL END
END

Như đã đề cập trong phần nhận xét của hàm này, độ phân biệt chữ hoa chữ thường của các phép so sánh ký tự sẽ tuân theo sự đối chiếu có hiệu lực. Theo mặc định, đối chiếu của SQL Server là đối chiếu sẽ dẫn đến so sánh không phân biệt chữ hoa chữ thường. Một cách để sửa đổi hàm này để luôn phân biệt chữ hoa chữ thường là thêm một đối chiếu cụ thể vào hai nơi mà các chuỗi được so sánh. Tuy nhiên, tôi đã không kiểm tra kỹ lưỡng điều này, đặc biệt là đối với các tác dụng phụ khi cơ sở dữ liệu đang sử dụng đối chiếu không mặc định. Đây là cách hai dòng sẽ được thay đổi để bắt buộc so sánh phân biệt chữ hoa chữ thường:

    -- prefix common to both strings can be ignored
    WHILE (@start < @sLen AND SUBSTRING(@s, @start, 1) = SUBSTRING(@t, @start, 1) COLLATE SQL_Latin1_General_Cp1_CS_AS) 

            SELECT @distance = 
                CASE WHEN (@sChar = SUBSTRING(@t, @j, 1) COLLATE SQL_Latin1_General_Cp1_CS_AS) THEN @diag                    --match, no change

1
Làm thế nào chúng ta có thể sử dụng điều này để tra cứu 5 chuỗi gần nhất hàng đầu trong một bảng? Ý tôi là giả sử tôi có bảng tên đường với 10m hàng. Tôi nhập tìm kiếm tên đường nhưng 1 ký tự bị viết sai. Làm cách nào để tra cứu 5 trận đấu gần nhất với hiệu suất tối đa?
MonsterMMORPG

1
Ngoài brute force (so sánh tất cả các địa chỉ), bạn không thể. Levenshtein không phải là thứ có thể dễ dàng tận dụng chỉ số. Nếu bạn có thể thu hẹp các ứng cử viên thành một tập hợp con nhỏ hơn thông qua thứ gì đó có thể được lập chỉ mục, chẳng hạn như mã zip cho địa chỉ hoặc mã phiên âm cho tên chẳng hạn, thì Levenshtein thẳng như vậy trong các câu trả lời ở đây có thể được áp dụng cho tập hợp con. Để áp dụng cho toàn bộ một tập hợp lớn, bạn cần phải đi đến một cái gì đó như Levenshtein Automata, nhưng việc triển khai điều đó trong SQL nằm ngoài phạm vi của câu hỏi SO đang được trả lời ở đây.
hatchhet - thực hiện với SOverflow

@MonsterMMORPG về lý thuyết, bạn có thể làm ngược lại và tính toán tất cả các hoán vị có thể có cho một khoảng cách Levenshtein nhất định. Hoặc bạn có thể thử và xem liệu các từ trong địa chỉ của bạn có tạo nên một danh sách đủ ngắn để hữu ích hay không (có thể bỏ qua những từ hiếm khi xuất hiện).
TheConstructor,

@MonsterMMORPG - điều này đã muộn, nhưng tôi nghĩ rằng tôi nên thêm một câu trả lời tốt hơn. Nếu bạn biết số lượng chỉnh sửa tối thiểu mà bạn sẽ cho phép, bạn có thể sử dụng phương pháp Symmetric Delete như đã được thực hiện trong dự án symspell trên github. Bạn có thể lưu trữ một tập con nhỏ các hoán vị chỉ xóa, rồi tìm kiếm bất kỳ trong tập hợp nhỏ các hoán vị xóa của chuỗi tìm kiếm. Trên tập hợp được trả về (sẽ nhỏ nếu bạn chỉ cho phép 1 hoặc 2 khoảng cách chỉnh sửa tối đa), sau đó bạn thực hiện toàn bộ quy tắc levenshtein. Nhưng điều đó sẽ ít hơn nhiều so với thực hiện trên tất cả các chuỗi.
rìu - thực hiện với SOverflow

1
@DaveCousineau - Như đã đề cập trong nhận xét hàm, so sánh chuỗi sử dụng phân biệt chữ hoa chữ thường cho đối chiếu SQL Server có hiệu lực. Theo mặc định, điều này thường có nghĩa là không phân biệt chữ hoa chữ thường. Xem chỉnh sửa cho bài đăng của tôi mà tôi vừa thêm. Việc triển khai Fribble trong một câu trả lời khác hoạt động tương tự liên quan đến đối chiếu.
rìu - thực hiện với SOverflow

58

Arnold Fribble có hai đề xuất trên sqlteam.com/forums

Đây là đứa trẻ từ năm 2006:

SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS ON 
GO

CREATE FUNCTION edit_distance_within(@s nvarchar(4000), @t nvarchar(4000), @d int)
RETURNS int
AS
BEGIN
  DECLARE @sl int, @tl int, @i int, @j int, @sc nchar, @c int, @c1 int,
    @cv0 nvarchar(4000), @cv1 nvarchar(4000), @cmin int
  SELECT @sl = LEN(@s), @tl = LEN(@t), @cv1 = '', @j = 1, @i = 1, @c = 0
  WHILE @j <= @tl
    SELECT @cv1 = @cv1 + NCHAR(@j), @j = @j + 1
  WHILE @i <= @sl
  BEGIN
    SELECT @sc = SUBSTRING(@s, @i, 1), @c1 = @i, @c = @i, @cv0 = '', @j = 1, @cmin = 4000
    WHILE @j <= @tl
    BEGIN
      SET @c = @c + 1
      SET @c1 = @c1 - CASE WHEN @sc = SUBSTRING(@t, @j, 1) THEN 1 ELSE 0 END
      IF @c > @c1 SET @c = @c1
      SET @c1 = UNICODE(SUBSTRING(@cv1, @j, 1)) + 1
      IF @c > @c1 SET @c = @c1
      IF @c < @cmin SET @cmin = @c
      SELECT @cv0 = @cv0 + NCHAR(@c), @j = @j + 1
    END
    IF @cmin > @d BREAK
    SELECT @cv1 = @cv0, @i = @i + 1
  END
  RETURN CASE WHEN @cmin <= @d AND @c <= @d THEN @c ELSE -1 END
END
GO

1
@Alexander, nó có vẻ hoạt động nhưng tôi sẽ thay đổi tên biến của bạn thành một cái gì đó có ý nghĩa hơn. Ngoài ra, tôi muốn loại bỏ @d, bạn biết độ dài của hai chuỗi trong đầu vào của bạn.
Lieven Keersmaekers

2
@Lieven: Nó không phải do tôi thực hiện, tác giả là Arnold Fribble. Tham số @d là sự khác biệt tối đa được phép giữa các chuỗi sau khi đạt đến mà chúng được coi là quá đa dạng và hàm trả về -1. Nó được thêm vào vì thuật toán trong T-SQL hoạt động quá chậm.
Alexander Prokofyev

Bạn nên kiểm tra mã psuedo của thuật toán tại: en.wikipedia.org/wiki/Levenshtein_distance, nó không được cải thiện nhiều.
Norman H

13

IIRC, với SQL Server 2005 trở lên, bạn có thể viết các thủ tục được lưu trữ bằng bất kỳ ngôn ngữ .NET nào: Sử dụng Tích hợp CLR trong SQL Server 2005 . Với điều đó, không khó để viết một thủ tục để tính khoảng cách Levenstein .

Xin chào, thế giới đơn giản! trích từ sự trợ giúp:

using System;
using System.Data;
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;

public class HelloWorldProc
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void HelloWorld(out string text)
    {
        SqlContext.Pipe.Send("Hello world!" + Environment.NewLine);
        text = "Hello world!";
    }
}

Sau đó, trong SQL Server của bạn chạy như sau:

CREATE ASSEMBLY helloworld from 'c:\helloworld.dll' WITH PERMISSION_SET = SAFE

CREATE PROCEDURE hello
@i nchar(25) OUTPUT
AS
EXTERNAL NAME helloworld.HelloWorldProc.HelloWorld

Và bây giờ bạn có thể chạy thử nó:

DECLARE @J nchar(25)
EXEC hello @J out
PRINT @J

Hi vọng điêu nay co ich.


7

Bạn có thể sử dụng Thuật toán khoảng cách Levenshtein để so sánh các chuỗi

Tại đây, bạn có thể tìm thấy một ví dụ T-SQL tại http://www.kodyaz.com/articles/fuzzy-string-matching-using-levenshtein-distance-sql-server.aspx

CREATE FUNCTION edit_distance(@s1 nvarchar(3999), @s2 nvarchar(3999))
RETURNS int
AS
BEGIN
 DECLARE @s1_len int, @s2_len int
 DECLARE @i int, @j int, @s1_char nchar, @c int, @c_temp int
 DECLARE @cv0 varbinary(8000), @cv1 varbinary(8000)

 SELECT
  @s1_len = LEN(@s1),
  @s2_len = LEN(@s2),
  @cv1 = 0x0000,
  @j = 1, @i = 1, @c = 0

 WHILE @j <= @s2_len
  SELECT @cv1 = @cv1 + CAST(@j AS binary(2)), @j = @j + 1

 WHILE @i <= @s1_len
 BEGIN
  SELECT
   @s1_char = SUBSTRING(@s1, @i, 1),
   @c = @i,
   @cv0 = CAST(@i AS binary(2)),
   @j = 1

  WHILE @j <= @s2_len
  BEGIN
   SET @c = @c + 1
   SET @c_temp = CAST(SUBSTRING(@cv1, @j+@j-1, 2) AS int) +
    CASE WHEN @s1_char = SUBSTRING(@s2, @j, 1) THEN 0 ELSE 1 END
   IF @c > @c_temp SET @c = @c_temp
   SET @c_temp = CAST(SUBSTRING(@cv1, @j+@j+1, 2) AS int)+1
   IF @c > @c_temp SET @c = @c_temp
   SELECT @cv0 = @cv0 + CAST(@c AS binary(2)), @j = @j + 1
 END

 SELECT @cv1 = @cv0, @i = @i + 1
 END

 RETURN @c
END

(Hàm do Joseph Gama phát triển)

Sử dụng :

select
 dbo.edit_distance('Fuzzy String Match','fuzzy string match'),
 dbo.edit_distance('fuzzy','fuzy'),
 dbo.edit_distance('Fuzzy String Match','fuzy string match'),
 dbo.edit_distance('levenshtein distance sql','levenshtein sql server'),
 dbo.edit_distance('distance','server')

Thuật toán chỉ đơn giản trả về số lượng stpe để thay đổi một chuỗi thành chuỗi khác bằng cách thay thế một ký tự khác ở một bước


Rất tiếc, điều này không bao gồm trường hợp chuỗi trống
Codeman

2

Tôi cũng đang tìm kiếm một ví dụ mã cho thuật toán Levenshtein và rất vui khi tìm thấy nó ở đây. Tất nhiên, tôi muốn hiểu thuật toán hoạt động như thế nào và tôi đã tìm hiểu một chút về một trong những ví dụ ở trên, tôi đã thử một chút đã được đăng bởi Veve . Để hiểu rõ hơn về mã, tôi đã tạo EXCEL bằng Ma trận.

khoảng cách cho FUZZY so với FUZY

Hình ảnh nói hơn 1000 từ.

Với EXCEL này, tôi thấy rằng có tiềm năng tối ưu hóa hiệu suất bổ sung. Tất cả các giá trị trong vùng màu đỏ phía trên bên phải không cần phải tính toán. Giá trị của mỗi ô màu đỏ dẫn đến giá trị của ô bên trái cộng với 1. Điều này là do, chuỗi thứ hai sẽ luôn dài hơn trong vùng đó so với chuỗi đầu tiên, điều gì làm tăng khoảng cách bằng giá trị 1 cho mỗi ký tự.

Bạn có thể phản ánh điều đó bằng cách sử dụng câu lệnh IF @j <= @i và tăng giá trị của @i Trước câu lệnh này.

CREATE FUNCTION [dbo].[f_LevenshteinDistance](@s1 nvarchar(3999), @s2 nvarchar(3999))
    RETURNS int
    AS
    BEGIN
       DECLARE @s1_len  int;
       DECLARE @s2_len  int;
       DECLARE @i       int;
       DECLARE @j       int;
       DECLARE @s1_char nchar;
       DECLARE @c       int;
       DECLARE @c_temp  int;
       DECLARE @cv0     varbinary(8000);
       DECLARE @cv1     varbinary(8000);

       SELECT
          @s1_len = LEN(@s1),
          @s2_len = LEN(@s2),
          @cv1    = 0x0000  ,
          @j      = 1       , 
          @i      = 1       , 
          @c      = 0

       WHILE @j <= @s2_len
          SELECT @cv1 = @cv1 + CAST(@j AS binary(2)), @j = @j + 1;

          WHILE @i <= @s1_len
             BEGIN
                SELECT
                   @s1_char = SUBSTRING(@s1, @i, 1),
                   @c       = @i                   ,
                   @cv0     = CAST(@i AS binary(2)),
                   @j       = 1;

                SET @i = @i + 1;

                WHILE @j <= @s2_len
                   BEGIN
                      SET @c = @c + 1;

                      IF @j <= @i 
                         BEGIN
                            SET @c_temp = CAST(SUBSTRING(@cv1, @j + @j - 1, 2) AS int) + CASE WHEN @s1_char = SUBSTRING(@s2, @j, 1) THEN 0 ELSE 1 END;
                            IF @c > @c_temp SET @c = @c_temp
                            SET @c_temp = CAST(SUBSTRING(@cv1, @j + @j + 1, 2) AS int) + 1;
                            IF @c > @c_temp SET @c = @c_temp;
                         END;
                      SELECT @cv0 = @cv0 + CAST(@c AS binary(2)), @j = @j + 1;
                   END;
                SET @cv1 = @cv0;
          END;
       RETURN @c;
    END;

Như đã viết, điều này sẽ không phải lúc nào cũng cho kết quả chính xác. Ví dụ: các đầu vào ('jane', 'jeanne')sẽ trả về khoảng cách là 3, khi khoảng cách phải là 2. Để sửa lỗi bổ sung này, mã bổ sung nên được thêm vào các hoán đổi @s1@s2nếu @s1có độ dài ngắn hơn @s2.
hatchhet - thực hiện với SOverflow.

2

Trong TSQL, cách tốt nhất và nhanh nhất để so sánh hai mục là các câu lệnh SELECT nối các bảng trên các cột được lập chỉ mục. Do đó, đây là cách tôi đề xuất thực hiện khoảng cách chỉnh sửa nếu bạn muốn hưởng lợi từ những lợi thế của công cụ RDBMS. Vòng lặp TSQL cũng sẽ hoạt động, nhưng tính toán khoảng cách Levenstein sẽ nhanh hơn bằng các ngôn ngữ khác so với TSQL để so sánh khối lượng lớn.

Tôi đã triển khai khoảng cách chỉnh sửa trong một số hệ thống bằng cách sử dụng chuỗi Nối với các bảng tạm thời chỉ được thiết kế cho mục đích đó. Nó đòi hỏi một số bước xử lý trước nặng nề - chuẩn bị các bảng tạm thời - nhưng nó hoạt động rất tốt với số lượng lớn các phép so sánh.

Nói một cách ngắn gọn: quá trình tiền xử lý bao gồm việc tạo, điền và lập chỉ mục các bảng tạm thời. Cái đầu tiên chứa id tham chiếu, cột một chữ cái và cột charindex. Bảng này được điền bằng cách chạy một loạt các truy vấn chèn chia từng từ thành các chữ cái (sử dụng CHỌN SUBSTRING) để tạo nhiều hàng như từ trong danh sách nguồn có các chữ cái (Tôi biết, đó là rất nhiều hàng nhưng máy chủ SQL có thể xử lý hàng tỷ hàng). Sau đó, tạo bảng thứ hai với cột 2 chữ cái, một bảng khác có cột 3 chữ cái, v.v. Kết quả cuối cùng là một loạt các bảng chứa id tham chiếu và chuỗi con của mỗi từ, cũng như tham chiếu vị trí của chúng trong từ.

Khi điều này được thực hiện, toàn bộ trò chơi là sao chép các bảng này và nối chúng với bản sao của chúng trong một truy vấn chọn GROUP BY tính số trận đấu. Điều này tạo ra một loạt các thước đo cho mọi cặp từ có thể có, sau đó được tổng hợp lại thành một khoảng cách Levenstein cho mỗi cặp từ.

Về mặt kỹ thuật, điều này rất khác so với hầu hết các cách triển khai khác của khoảng cách Levenstein (hoặc các biến thể của nó), vì vậy bạn cần hiểu sâu sắc cách hoạt động của khoảng cách Levenstein và tại sao nó được thiết kế như vậy. Điều tra các lựa chọn thay thế cũng bởi vì với phương pháp đó, bạn sẽ có một loạt các chỉ số cơ bản có thể giúp tính toán nhiều biến thể của khoảng cách chỉnh sửa cùng một lúc, cung cấp cho bạn những cải tiến tiềm năng thú vị về học máy.

Một điểm khác đã được đề cập bởi các câu trả lời trước trong trang này: cố gắng xử lý trước càng nhiều càng tốt để loại bỏ các cặp không yêu cầu đo khoảng cách. Ví dụ, một cặp hai từ không có một chữ cái chung nào nên được loại trừ, vì khoảng cách chỉnh sửa có thể thu được từ độ dài của các chuỗi. Hoặc không đo khoảng cách giữa hai bản sao của cùng một từ, vì bản chất nó là 0. Hoặc loại bỏ các từ trùng lặp trước khi thực hiện phép đo, nếu danh sách các từ của bạn đến từ một văn bản dài thì có khả năng các từ giống nhau sẽ xuất hiện nhiều hơn một lần, vì vậy việc đo khoảng cách chỉ một lần sẽ tiết kiệm thời gian xử lý, v.v.

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.