Algorithm 如何从n个元素中找到k置换的索引?

Algorithm 如何从n个元素中找到k置换的索引?,algorithm,permutation,Algorithm,Permutation,我知道,对于大小为k的k排列p,由n元素构建,有: P(n, k) = n! / (n - k)! 可能的k-置换。例如: k = 2 n = 4 l = [1, 2, 3, 4] P(n, k) = 4! / (4 - 2)! = 12 1 2 | 2 1 | 3 1 | 4 1 1 3 | 2 3 | 3 2 | 4 2 1 4 | 2 4 | 3 4 | 4 3 k = 3 n = 4 l = [1, 2, 3, 4] P(n, k) = 4! / (4 - 3)! = 24 1

我知道,对于大小为
k
k
排列
p
,由
n
元素构建,有:

P(n, k) = n! / (n - k)!
可能的
k
-置换。例如:

k = 2
n = 4
l = [1, 2, 3, 4]
P(n, k) = 4! / (4 - 2)! = 12

1 2 | 2 1 | 3 1 | 4 1
1 3 | 2 3 | 3 2 | 4 2
1 4 | 2 4 | 3 4 | 4 3
k = 3
n = 4
l = [1, 2, 3, 4]
P(n, k) = 4! / (4 - 3)! = 24

1 2 3 | 2 1 3 | 3 1 2 | 4 1 2
1 2 4 | 2 1 4 | 3 1 4 | 4 1 3
1 3 2 | 2 3 1 | 3 2 1 | 4 2 1
1 3 4 | 2 3 4 | 3 2 4 | 4 2 3
1 4 2 | 2 4 1 | 3 4 1 | 4 3 1
1 4 3 | 2 4 3 | 3 4 2 | 4 3 2
还有一个例子:

k = 2
n = 4
l = [1, 2, 3, 4]
P(n, k) = 4! / (4 - 2)! = 12

1 2 | 2 1 | 3 1 | 4 1
1 3 | 2 3 | 3 2 | 4 2
1 4 | 2 4 | 3 4 | 4 3
k = 3
n = 4
l = [1, 2, 3, 4]
P(n, k) = 4! / (4 - 3)! = 24

1 2 3 | 2 1 3 | 3 1 2 | 4 1 2
1 2 4 | 2 1 4 | 3 1 4 | 4 1 3
1 3 2 | 2 3 1 | 3 2 1 | 4 2 1
1 3 4 | 2 3 4 | 3 2 4 | 4 2 3
1 4 2 | 2 4 1 | 3 4 1 | 4 3 1
1 4 3 | 2 4 3 | 3 4 2 | 4 3 2
那么,我如何找到
k
-排列
p
的索引呢?考虑排列 按字典顺序生成

编辑: 首先,我可以找到
p
所在的“块”,通过
p
的第一个元素对块进行寻址。例如,对于
p=[3,2,4]
p
的索引应至少为12(从0到
p(n,k)-1计数)

但是,为了找到“块”中的第二个元素,我必须看看剩余的项目是什么,以及它们将位于哪个位置。我的意思是,我最终将处理列表
[1,4]
,4将位于位置2,因此简单地使用元素作为键将需要一些额外的操作

我可以使用散列来查找元素并更新它们的位置,但它会给我一个
O(n^2)
时间复杂度。有没有可能做得更好

然而,这是蛮力;当然,
pos
对排列的结构一无所知。

使用(BST)。在开始之前将所有号码存储在其中,并在使用后将其删除。要查找第x个元素,请为每个顶点维护
。子树化
,然后从树下找到所需的编号。伪代码:

    def findXth(x):
        curVertex = BST.root
        while:
            curPosition = curVertex.leftChild.subtreeSize
            if curPosition == x: return curVertex.value
            elif curPosition > x: curVertex = curVertex.leftChild
            elif curPosition < x: curVertex = curVertex.rightChild
def findXth(x):
curVertex=BST.root
而:
curPosition=curVertex.leftChild.subtreeSize
如果curPosition==x:返回curVertex.value
elif curPosition>x:curVertex=curVertex.leftChild
elif curPosition
不要忘记检查顶点是否存在,并删除找到的顶点


整体复杂度将是O(n logn)。

给定位置上给定数字的排列数由公式给出 (n位位置)!/(n-k)!其中,数字位置从左侧1开始

要获得给定置换(即其索引)的前置置换数,请将每个数字的公式乘以尚未使用的前置位数,然后将其相加

例1,k=2,n=4,p=[3,4]

第一位数字,3: (4-1)! / (4-2)! * (未使用的前一位数,2)=6 在第一个排列之前有六个排列,位置1中有3个

第二位数字,4: (4-2)! / (4-2)! * (未使用的前一位数,2)=2 在第一个排列之前有两个排列,位置2有4

零基索引:6+2=8

例2,k=3,n=4,p=[3,2,4]

第一位数字,3: (4-1)! / (4-3)! * (未使用的前一位数,2)=12 在第一个排列之前有12个排列,位置1中有3个

第二位数字,2: (4-2)! / (4-3)! * (未使用的前一位数,1)=2 在第一个排列之前有两个排列,位置2为2

第三位数字,4: (4-3)! / (4-3)! * (未使用的前一位数,1)=1 在第一个排列之前有一个排列,其位置3中有4


零基索引:12+2+1=15。

您可以参考以下函数

 /**
 *  list all k or <=k size permutation of a given list with n unique elements.
 *  n can be bigger than 64. this function will take O(K^N) time, Bad.
 *
 * @param uniqueList
 * @param permutationSize
 * @param permutation
 * @param only            Only show the permutation of permutationSize,
 *                        else show all permutation of less than or equal to permutationSize.
 */
public static void my_permutationOf(List<Integer> uniqueList, int permutationSize, List<Integer> permutation, boolean only) {
    if (permutation == null) {
        assert 0 < permutationSize && permutationSize <= uniqueList.size();
        permutation = new ArrayList<>(permutationSize);
        if (!only) {
            System.out.println(Arrays.toString(permutation.toArray()));
        }
    }
    for (int i : uniqueList) {
        if (permutation.contains(i)) {
            continue;
        }
        permutation.add(i);
        if (!only) {
            System.out.println(Arrays.toString(permutation.toArray()));
        } else if (permutation.size() == permutationSize) {
            System.out.println(Arrays.toString(permutation.toArray()));
        }
        if (permutation.size() < permutationSize) {
            my_permutationOf(uniqueList, permutationSize, permutation, only);
        }
        permutation.remove(permutation.size() - 1);
    }
}

谢谢你的回答。请检查我的编辑。我忘了补充到目前为止我所想的,以及我想要的答案;您正在寻找一个快速算法,而不仅仅是任何旧代码来给出答案。可能的起点:@Kaz非常感谢您的参考。我会检查它,我也会尝试使用TXR。我正在考虑向Lisp做自我介绍,现在我将尝试Lisp和TXR。谢谢。非常感谢你的回答!真棒的解决方案!谢谢你教我这个虽然起初我认为这是线性的,即使是在最坏的情况下,但现在我想这还不止于此。未使用的前位数函数的时间复杂度是多少?除非它是常数,否则最坏的情况可能是对数(使用一些二进制搜索结构)或线性。我想不出任何方法可以在
O(1)
中保留一个未使用的前几位数字表。“你能想出解决办法吗?”鲁本斯说,这是另一个有趣的问题。让我想想。
n
能有多大?实际上
n
相当小,目前最大值为25。尽管大小很大,我想这可能有助于产生一些特定于语言的解决方案(比如在c/c++中使用位计数),但我还是非常好奇是否有可能在
O(1)
中派生出该方法。很高兴看到你也感兴趣!:我能帮忙吗?
[0, 1, 2]
[0, 1, 3]
[0, 2, 1]
[0, 2, 3]
[0, 3, 1]
[0, 3, 2]
[1, 0, 2]
[1, 0, 3]
[1, 2, 0]
[1, 2, 3]
[1, 3, 0]
[1, 3, 2]
[2, 0, 1]
[2, 0, 3]
[2, 1, 0]
[2, 1, 3]
[2, 3, 0]
[2, 3, 1]
[3, 0, 1]
[3, 0, 2]
[3, 1, 0]
[3, 1, 2]
[3, 2, 0]
[3, 2, 1]