Javascript 查找未排序列表和已排序列表之间的最小距离
设A为列表,S为相同元素的排序列表。假设所有元素都不同。如何找到将a转换为S的最小“移动”(Javascript 查找未排序列表和已排序列表之间的最小距离,javascript,python,algorithm,language-agnostic,Javascript,Python,Algorithm,Language Agnostic,设A为列表,S为相同元素的排序列表。假设所有元素都不同。如何找到将a转换为S的最小“移动”(在Y(或结束)之前移动X)集合 示例: A = [8,1,2,3] S = [1,2,3,8] A => S requires one move: move 8 before end A = [9,1,2,3,0] S = [0,1,2,3,9] A => S requires two moves: move 9 before 0 move 0 before 1 我
在Y(或结束)之前移动X
)集合
示例:
A = [8,1,2,3]
S = [1,2,3,8]
A => S requires one move:
move 8 before end
A = [9,1,2,3,0]
S = [0,1,2,3,9]
A => S requires two moves:
move 9 before 0
move 0 before 1
我更喜欢javascript或python,但任何语言都可以。这个问题相当于这个问题 您必须定义一个比较运算符
less
<当且仅当目标序列中a
在b
之前时,code>less(a,b)将返回true
。现在使用这个比较运算符,计算源序列的最大递增子序列。您必须移动不属于此子序列的每个元素(否则子序列将不会是最大值),并且您可以将其仅移动一次(将其移动到其目标位置)
编辑:根据amit的要求,以下是我对上述陈述的证明:
让我们表示目标序列B
,并让我们表示源序列A
。设n=|A |
和k
为上述最长递增序列的长度
- 让我们假设可以从
到达A
,移动次数少于B
。这意味着将不会移动n-k
中的至少A
元素。设s1、s2、…sm为未移动的元素集。根据这个假设,我们知道n-k+1
。由于这些元素没有移动,它们之间的相对位置就不会改变。因此,所有这些元素在目标序列m>k
中的相对位置与B
中的相对位置相同。因此,对于任何A
,i
,上述定义的运算符less(si,sj)应为真。但如果这是真的,那么s1,s2,…sm是递增序列,而asj
这与k是最长递增序列长度的假设相矛盾m>k
- 现在,让我们展示一种算法,通过移动所有元素(但不包括最长递增序列的元素),从
到达A
。我们将按元素在B中出现的顺序移动元素。我们不会移动作为最长递增序列一部分的元素。如果当前元素是B中的第一个元素,我们只需将其移动到序列的开头。否则,我们将当前元素向右移动到B中上一个元素的位置之后。请注意,此元素可能是我们移动的上一个元素,也可能是最长递增序列中的元素。请注意,在每个步骤中,当我们将要移动索引为B
的元素时,索引为i
的所有元素都将彼此具有正确的相对位置李>1、2、…i-1
transform(a,s)
,它接受两个参数-如语句中所述列出a和b。首先,我将创建一个映射positions
,将a
中的每个元素映射到其在s中的位置:
var positions = {};
for (var i = 0; i < a.length; ++i) {
positions[a[i]] = i;
}
现在,我将不描述如何在a
中找到关于该比较运算符的最大递增子序列。你可以看看详细的解释如何做到这一点。我将简单地假设我定义了一个函数:
function max_increasing_subsequence(a, positions)
它返回a
中相对于上面定义的比较运算符less
的最大递增子序列(使用位置
)作为列表。我将用你的第二个例子来说明我们目前的情况:
A = [9,1,2,3,0]
S = [0,1,2,3,9]
位置中的值如下所示:
positions = { 0 : 0,
1 : 1,
2 : 2,
3 : 3,
9 : 4}
max\u递增子序列(a,位置)
的结果将是[1,2,3]
。顺便说一下,如果a
中可能存在重复元素,则最好返回索引,而不是max\u increasing\u subsequence
中的元素(在此特定示例中,差异将不可见)
现在,我将创建另一个帮助器映射,以指示最大递增子序列中包含的元素:
var included = {};
l = max_increasing_subsequence(a, positions);
for (var i = 0; i < l.length; ++i) {
included[l[i]] = true;
}
请注意,在上面的解决方案中,我假设每次记录新命令时,都是在执行所有以前的命令之后,根据数组a
的顺序进行记录
所以总的来说,我认为转换应该是这样的:
function transform(a, s) {
var positions = {};
for (var i = 0; i < a.length; ++i) {
positions[a[i]] = i;
}
var included = {};
l = max_increasing_subsequence(a, positions);
var included = {};
for (var i = 0; i < l.length; ++i) {
included[l[i]] = true;
}
if (!(s[s.length - 1] in included)) {
console.log("Move" + s[s.length - 1] + " at the end");
}
for (var i = s.length - 2; i >= 0; --i) { // note s.length - 2 - don't process last element
if (!(s[i] in included)) {
console.log("Move" + s[i] + " before " + s[i + 1]);
}
}
}
函数变换(a,s){
var位置={};
对于(变量i=0;i=0;--i){//注意s.length-2-不要处理最后一个元素
如果(!(包括s[i]){
log(“移动”+s[i]+”到“+s[i+1]);
}
}
}
我希望这段代码能让我的答案更清楚 如果您将两个列表视为两个字符串(例如,数字是ASCII编码中的值),那么问题就相当于找到允许您将第一个字符串转换为第二个字符串的操作。操作的数量依次是字符串之间的Levenshtein或edit距离 可以通过在矩阵中存储两个字符串的所有前缀之间的距离,然后回溯步骤,在矩阵的每一行找到最佳操作(需要最少操作才能到达的操作) @IvayloStrandjev提出的最长递增子序列算法与最长公共子序列问题有关,而最长公共子序列问题又与最长公共子序列问题有关
if (!(s[s.length - 1] in included)) {
console.log("Move" + s[s.length - 1] + " at the end");
}
for (var i = s.length - 2; i >= 0; --i) {
if (!(s[i] in included)) {
console.log("Move" + s[i] + " before " + s[i + 1]);
}
}
function transform(a, s) {
var positions = {};
for (var i = 0; i < a.length; ++i) {
positions[a[i]] = i;
}
var included = {};
l = max_increasing_subsequence(a, positions);
var included = {};
for (var i = 0; i < l.length; ++i) {
included[l[i]] = true;
}
if (!(s[s.length - 1] in included)) {
console.log("Move" + s[s.length - 1] + " at the end");
}
for (var i = s.length - 2; i >= 0; --i) { // note s.length - 2 - don't process last element
if (!(s[i] in included)) {
console.log("Move" + s[i] + " before " + s[i + 1]);
}
}
}
import argparse
import numpy as np
class Levenshtein(object):
def __init__(self, string1, string2):
self.string1 = string1
self.string2 = string2
self.scores_matrix = np.zeros(
(len(self.string1) + 1, len(self.string2) + 1), dtype=np.int16)
self.operations_matrix = np.empty_like(
self.scores_matrix, dtype=(np.str_, 16))
self.total_steps = 0
def distance(self):
m = len(self.string1) + 1
n = len(self.string2) + 1
for i in range(m):
self.scores_matrix[i, 0] = i
for j in range(n):
self.scores_matrix[0, j] = j
for j in range(1, n):
for i in range(1, m):
if self.string1[i - 1] == self.string2[j - 1]:
self.scores_matrix[i, j] = self.scores_matrix[i - 1, j - 1]
self.operations_matrix[i, j] = 'match'
else:
self.scores_matrix[i, j] = self.select_operation(i, j)
if j == n - 1: # a row is complete
self.determine_best_op_and_print(i)
return self.scores_matrix[m - 1, n - 1]
def select_operation(self, i, j):
possible_ops = ['delete', 'insert', 'substitute']
ops_scores = [
self.scores_matrix[i - 1, j] + 1, # deletion
self.scores_matrix[i, j - 1] + 1, # insertion
self.scores_matrix[i - 1, j - 1] + 1] # substitution
chosen_op = min(ops_scores)
chosen_op_name = possible_ops[ops_scores.index(chosen_op)]
self.operations_matrix[i, j] = chosen_op_name
return chosen_op
def determine_best_op_and_print(self, i):
reversed_row = self.scores_matrix[i][::-1]
reversed_pos_min = np.argmin(reversed_row)
pos_min = len(self.scores_matrix[i]) - (reversed_pos_min + 1)
best_op_name = self.operations_matrix[i, pos_min]
if best_op_name != 'match':
self.total_steps += 1
print best_op_name, self.string1[i - 1], self.string2[pos_min - 1]
def parse_cli():
parser = argparse.ArgumentParser()
parser.add_argument('--list', nargs='*', required=True)
return parser.parse_args()
if __name__ == '__main__':
args = parse_cli()
A = args.list
S = sorted(A)
lev = Levenshtein(A, S)
dist = lev.distance()
print "{} total steps were needed; edit distance is {}".format(
lev.total_steps, dist)
$ python levenshtein.py --list 8 1 2 3
substitute 8 1
1 total steps were needed; edit distance is 2
$ python levenshtein.py --list 9 1 2 3 0
substitute 9 0
substitute 0 9
2 total steps were needed; edit distance is 2
sequence = [seq2.index(element) for element in seq]
if len(sequence) <= 1:
return 0, sequence
else:
firstHalf = sequence[:int(len(sequence)/2)]
secondHalf = sequence[int(len(sequence)/2):]
count1, firstHalf = mergeSortInversionCount(firstHalf)
count2, secondHalf = mergeSortInversionCount(secondHalf)
firstN = len(firstHalf)
secondN = len(secondHalf)
secondHalfEnd = secondN
count3 = count1 + count2
# Count the inversions in the merge
# Uses a countdown through each sublist
for i in xrange(firstN-1, -1, -1):
x = firstHalf[i]
inversionFound = False
for j in xrange(secondHalfEnd-1,-1,-1):
if x > secondHalf[j]:
inversionFound = True
break
if inversionFound:
secondHalfEnd = j+1
count3 += j+1
mergeList = firstHalf + secondHalf
mergeList.sort()
return count3, mergeList