是否有一个O(n)或更快的算法用于通过levenshtein距离对列表进行排序?我在SO上看了一些解决方案,但他们都调用了传统的排序方式。现在,假设您只是对输入的字节求和:您将获得几乎按其levenshtein距离排序的哈希键。例如,我获取了一组随机字符串,并通过字节求和计算它们的哈希值:
[ { hash: 2826, val: 'LKAMFKLFUAHUHAUHAUHAU:ANGONEGANAILFJAL:' },
{ hash: 2829, val: 'LKAMFKLFLFUAHUAHUHAUAHANGONEGANAILFJAL:' },
{ hash: 2845, val: 'LKAMFKLFLFAKAKKAKAfiO:ANGONEGANAILFJAL:' },
{ hash: 3064, val: 'LKAMFKLFKKKlaNflanfiO:ANGONEGANAILFJAL:' },
{ hash: 3092, val: 'LKAMFKLFLFklaNflanfiO:ANGONEGANAILFJAL:' },
{ hash: 3203, val: 'LKAMFKLFLFklaNflanfiRSRSRSRSRRNAILFJAL:' },
{ hash: 3249, val: 'LKNFUU{N{UAFN{NF}FNPNF{FN{APNF{WNFF{NF' },
{ hash: 3843, val: 'ddddddddddaaaaaaaaaddddddddddaaaaaaaaaa' },
{ hash: 3858, val: 'safndjnjcmxn,znv,mnm,n,mvnm,vn,mznv,mvv' },
{ hash: 3934, val: 'nngnangngdsgsangnanwns.mnv.nv.xnjvnsf.,' },
{ hash: 3972, val: 'adadsadadsadadadsadsadadsadsadadsadsada' },
{ hash: 3992, val: 'adsadadadsadasdasdafadfasfdsafsafasfafd' },
{ hash: 4041, val: 'asfdsafasdfsafafasdfasdfafsdfdasfasfasf' },
{ hash: 4047, val: 'kkkkkkkkkkkdddddddddkkkkkkkkkkddddddddd' },
{ hash: 4058, val: 'jfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj' },
{ hash: 4081, val: 'ioudnjkanfjfhjhfjhakfshfkjhdajhkjafhkjf' },
{ hash: 4082, val: 'ioudnjkanfjfhjhfjhakfshfkjhdakhkjafhkjf' },
{ hash: 4082, val: 'oisdnkbgjkbajkgkbgkjbkklgjklsbkbfkjafas' },
{ hash: 4090, val: 'ioudnjsanfjfhjhfjhakfshfkjhdakhkjafhkjf' },
{ hash: 4099, val: 'asldfjlkcmclmasldkkjflksajflkjaljfljlfa' },
{ hash: 4101, val: 'sidfjlasjflijflijlfjliafjdlifjlijfiljfl' },
{ hash: 4105, val: 'iousnjsanfjfhjhfjhakfshfkjhdakhkjafhkjf' },
{ hash: 4125, val: 'iousnjsanfjfhlhfjuakfshkkjhdakhkjafhkjf' },
{ hash: 4128, val: 'sadnfjnfjnjfnjsdfnjafnjkfnkfnjkansdfjkn' },
{ hash: 4143, val: 'dnsfanfjknasfjklnaskfnkfnklafnjkfnkldsn' },
{ hash: 4150, val: 'dskfoisandginsgnlgn:nglngbtbiybuburubsu' },
{ hash: 4155, val: 'afadfsfsfsdfffsfsfsfsdfsfsfsdfsfsfsfsfs' },
{ hash: 4166, val: 'kjdkljkljkljlkjkljlkjlkjlkjlkjljlkjljlk' },
{ hash: 4211, val: 'jsanjnvjksnfkjsanfuiawngingiuilugniugng' },
{ hash: 4229, val: 'kllnlknlknklnklnlnlknknklnlnlnklnlknlkn' },
{ hash: 4238, val: 'lsniorhgpwoiqutoiuieofnionofnoinfonfioa' },
{ hash: 4349, val: 'iasfioehwoptqpoituopqwtuoquweporuqiorur' },
{ hash: 4374, val: 'ioequroiqwuroiuriouroiuopriuprouqpourrq' },
{ hash: 4377, val: 'iiuouoiuoiuouoiuuououoiuououoiuououoiuo' } ]
结果几乎已经排序,这意味着插入排序可以非常快地完成作业(see)。
如果这样粗略的实验提供了那些结果,那么肯定会有一些解决方案,它的答案就缺少了。这可能是什么?
答案 0 :(得分:3)
下面的讨论是我啰嗦的说法,你的想法(据我所知)在一般情况下不起作用。原因?因为两个长度为N的字符串之间的Levenshtein距离应为N,但字符串具有相同的校验和。例如,反向字符串。此外,Levenshtein距离为1的两个字符串之间的校验和差异可以是255(或Unicode中的65,536)。有了这样的范围,“几乎整理”,即使你能以某种方式(见下文),也不会有太大的收获。
所以你注意到了一个简单的校验和和Levenshtein距离之间的相关性。这是一个明显的关系。如果两个字符串之间的Levenshtein距离很小,那么这两个字符串大多包含相同的字符。因此,简单校验和的计算将导致非常相似的值。有时。
然而,正如其他人所指出的那样,情况恰恰相反。字符串abcdef
和fedcba
具有相同的校验和,但对于这么短的字符串,它们的Levenshtein距离相当大。
这不仅仅是逆转。例如,考虑字符串00000000
。字符串0000000~
将具有比11111111
更大的校验和,即使Lev。距离要小得多。
我认为你会在一般情况下发现校验和与列夫之间的关系。距离是...有时巧合。但是,让我们忽略那个特定的问题,然后继续你的关于排序的假设。
据我所知(并且,实际上,你的问题在这一点上并不完全清楚),你想根据他们的Levenshtein距离对字符串列表进行排序。你没有说是什么的距离,但我会假设你有一个起始字符串,S
,一堆其他字符串[S1, S2, S3, etc.]
,你想要排序Lev的其他字符串列表。与S
的距离。
您的假设似乎是为每个字符串计算一个简单的校验和将允许您更快地进行排序。
问题是,一旦你计算了校验和,就必须对它们进行排序。这将花费O(n log n)
时间与传统的比较排序(在任何情况下,如果你有一个特殊目的排序,至少O(n)
时间。一旦你有了所谓的几乎有序的列表,你就必须计算Lev。无论如何距离,然后重新排列列表顺序以反映真实距离。但有什么意义呢?
你必须计算列弗。无论如何,您将花费至少 O(n)
时间排序某些内容。当你可以更快地计算Lev时,为什么要去计算和排序校验和的额外麻烦。距离并对它们进行排序?
答案 1 :(得分:1)
O(n log n)绑定用于特定类型的排序,基于对有序类型的比较。
在这种情况下,您的排序基于一个简单的无符号整数值(取决于您正在处理的数据)可能是一个相当小的界限。在这种情况下,您的选项是......
如果最大距离足够小,则创建一个(最初为空)列表头指针数组。数组下标是距离。遍历您的数据以填充该列表数组,然后按顺序提取所有数据。如果您担心数组中的许多头指针保持为空(许多距离从未发生过),您还可以在数组中构建两个双链表 - 一个最初是未使用项的完整列表,一个最初为空列表二手物品。这样,当您提取数据时,您只需要查看那些包含项目的列表。
无论最大距离如何,您都可以使用哈希表执行相同的操作。如果每次需要更多空间时表格以常数因子增长,则每个插入需要O(1)时间摊销。当你考虑整个循环时,那就变成简单的O(n) - 不再摊销 - 因为定义了“摊销”的方式。哈希表通常是无序的,但你可以作弊 - 哈希就是距离。在提取数据时可能需要更多的作弊来避免多次传递,但这不应该太难。
我认为尝试使用校验和没有任何好处。
如果您想对数据进行排序,则无法击败O(n),因为您可能需要移动每个项目。即使你只是神奇地知道将每个项目移动到哪里,无论如何,做这些动作都是O(n)。
此外,即使数据已经按照正确的顺序排列,只需计算确认的距离也是O(n)。
我有点紧张,在第二个想法,因为你不能只是将一个Levenshtein距离分配给一个字符串 - 它相对于另一个字符串。
如果你想建立一个字符串索引,以便你可以搜索“最近的”字符串,你应该看看this post about Vantage Point Trees on Steve Hanov's blog。
我怀疑你会使用它获得O(n)性能。