Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 排序编号的最小特殊移动次数_Algorithm_Sorting_Reorderlist - Fatal编程技术网

Algorithm 排序编号的最小特殊移动次数

Algorithm 排序编号的最小特殊移动次数,algorithm,sorting,reorderlist,Algorithm,Sorting,Reorderlist,给出了一系列的数字 1 15 2 5 10 我需要获得 1 2 5 10 15 我能做的唯一操作是“将数字X移动到位置Y” 在上面的示例中,我只需要执行“将数字15移动到位置5” 我想尽量减少操作的数量,但考虑到可用的操作,我找不到/记不起一个经典的算法 一些背景: 我正在与一个API进行交互,以获得类似看板的服务 我有大约600张卡片,我们的bug跟踪器上的一些操作可能意味着看板中这600张卡片的重新排序(如果一个项目的优先级发生变化,多张卡片可以同时移动) 我可以通过600次API调用

给出了一系列的数字

1 15 2 5 10
我需要获得

1 2 5 10 15

我能做的唯一操作是“将数字X移动到位置Y”

在上面的示例中,我只需要执行“将数字15移动到位置5”

我想尽量减少操作的数量,但考虑到可用的操作,我找不到/记不起一个经典的算法

一些背景:

  • 我正在与一个API进行交互,以获得类似看板的服务
  • 我有大约600张卡片,我们的bug跟踪器上的一些操作可能意味着看板中这600张卡片的重新排序(如果一个项目的优先级发生变化,多张卡片可以同时移动)
  • 我可以通过600次API调用来完成,但我正在尽可能减少这个数量

一种可能的解决方案是查找并仅移动不在其中的元素

我不能证明它是最优的,但很容易证明它是正确的,并且比N交换更好

下面是Python 2中的概念证明。我将其实现为一个O(n2)算法,但我非常确定它可以简化为O(n logn)


引理:可以对列表L进行排序(按递增顺序)的最小(删除元素、插入元素)对数为:

Smin(L)=| L |-| LIC(L)|

其中LIC(L)是最长的递增子序列

因此,你必须:

  • 建立列表的LIC
  • 移除不在其中的元素,并将它们插入回适当的位置(使用二进制搜索)

证明: 通过归纳法

对于大小为1的列表,最长的递增子序列是长度。。。1.该列表已排序,因此需要(del,ins)对的数量为

|L |-| LIC(L)|=1-1=0

现在让Ln是一个长度为n,1的列表≤ N设Ln+1是通过在Ln的左侧添加元素en+1而获得的列表。
该元素可能会也可能不会影响最长的递增子序列。让我们看看如何

设in,1和in,2为LIC(Ln)(*)的前两个元素:

  • 如果en+1>in,2,则LIC(Ln+1)=LIC(Ln)
  • 如果en+1≤ in,1,然后LIC(Ln+1)=en+1 | | LIC(Ln)
  • 否则,LIC(Ln+1)=LIC(Ln)-in,1+en+1。我们保留第一个元素最高的LIC。通过从LIC上拆下in,1并将其更换为en+1来完成此操作

在第一种情况下,我们删除en+1,从而对Ln进行排序。根据归纳假设,这需要n(删除,插入)对。然后我们必须在适当的位置插入en+1。因此:

S(Ln+1)min=1+S(Ln)min S(Ln+1)min=1+n-| LIC(Ln)| S(Ln+1)min=|Ln+1 |-LIC(Ln+1)|


在第二种情况下,我们忽略en+1。我们首先删除LIC(Ln)中没有的元素。这些元素必须再次插入!有

S(Ln)min=|Ln |-LIC(Ln)|

这些因素

现在,我们只需要小心地按正确的顺序插入它们(相对于en+1)。最后,它需要:

S(Ln+1)min=|Ln |-LIC(Ln)| S(Ln+1)min=|Ln |+1-(| LIC(Ln)|+1)

由于我们有| LIC(Ln+1)|=| LIC(Ln)|+1和| Ln+1 |=| Ln |+1,我们最后有:

S(Ln+1)min=|Ln+1 |-LIC(Ln+1)|


最后一种情况可以通过考虑从Ln+1中移除in,1得到的列表L'n来证明。在这种情况下,LIC(L'n)=LIC(Ln+1),因此:

|LIC(L'n)|=| LIC(Ln)|(1)

从那里,我们可以通过归纳假设对L'n(采用| L'n |-| LIC(L'n))进行排序。前面的等式(1)导致结果


(*):如果LIC(Ln)<2,那么in,2不存在。忽略与它的比较。在这种情况下,只有情况2和情况3适用…结果仍然有效

在meMaybe看来,这似乎是一个XY问题,我遗漏了一些东西-但是这个问题与基于交换的“标准”排序(如quicksort)有什么区别?“将数字X移到位置Y”-插入排序?@amit我认为对集合进行排序所需的比较数量与交换的实际数量之间存在差异(在他的情况下,实际上不是交换,但这是一个类似的原则)。对数组进行排序最多需要N次交换,但根据数据的排列周期,可以用更少的时间完成。我明白了,他只想最小化交换的数量,而不是操作的总数。谢谢@JuanLopesI。我认为他没有实际的“交换”操作,更像是“从X中删除”和“在Y中添加”"@JuanLopes哦…在这种情况下,我的答案是无用的,我想:我认为它是最优的,如果我没有弄错的话,它可以通过归纳法证明。这只有在数字序列不是完全随机的情况下才有帮助。@Techfist你是什么意思?我的意思是,这种方法只有在序列确实包含递增的子序列,其他子序列时才会给出最优答案wise不会这么做,但如果他最初的问题只是对他提供的数字进行排序,那么这个解决方案似乎是最优的。如果数据中没有长度大于1的递增子序列(即数组按降序排序),则无法对移动次数少于N-1的数据进行排序。@Rerito刚刚证实了这一点。
from operator import itemgetter

def LIS(V):
    T = [1]*(len(V))
    P = [-1]*(len(V))

    for i, v in enumerate(V):
        for j in xrange(i-1, -1, -1):
            if T[j]+1 > T[i] and V[j] <= V[i]:
                T[i] = T[j] + 1
                P[i] = j

    i, _ = max(enumerate(T), key=itemgetter(1))
    while i != -1:
        yield i
        i = P[i]

def complement(L, n):
    for a, b in zip(L, L[1:]+[n]):
        for i in range(a+1, b):
            yield i

def find_moves(V):
    n = len(V)
    L = list(LIS(V))[::-1]
    SV = sorted(range(n), key=lambda i:V[i])
    moves = [(x, SV.index(x)) for x in complement(L, n)]

    while len(moves):
        a, b = moves.pop()
        yield a, b
        moves = [(x-(x>a)+(x>b), y) for x, y in moves]


def make_and_print_moves(V):
    print 'Initial array:', V

    for a, b in find_moves(V):
        x = V.pop(a)
        V.insert(b, x)        
        print 'Move {} to {}. Result: {}'.format(a, b, V)
    print '***'

make_and_print_moves([1, 15, 2, 5, 10])

make_and_print_moves([4, 3, 2, 1])

make_and_print_moves([1, 2, 4, 3])
Initial array: [1, 15, 2, 5, 10]
Move 1 to 4. Result: [1, 2, 5, 10, 15]
***
Initial array: [4, 3, 2, 1]
Move 3 to 0. Result: [1, 4, 3, 2]
Move 3 to 1. Result: [1, 2, 4, 3]
Move 3 to 2. Result: [1, 2, 3, 4]
***
Initial array: [1, 2, 4, 3]
Move 3 to 2. Result: [1, 2, 3, 4]
***