如何计算给定排列的词典排名

时间:2015-12-11 18:32:46

标签: algorithm math language-agnostic permutation combinatorics

例如,房间里有6把椅子,有4个女孩和2个男孩。他们可以通过15种独特的方式坐在这把椅子上6!/(4!*2!)=15

我的问题是找到有效的方法来计算他们选择坐的可能性的位置。按位置我的意思是:

BBGGGG - possible position #1
BGBGGG - possible position #2
BGGBGG - possible position #3
BGGGBG - possible position #4
BGGGGB - possible position #5
GBBGGG - possible position #6
GBGBGG - possible position #7
GBGGBG - possible position #8
GBGGGB - possible position #9
GGBBGG - possible position #10
GGBGBG - possible position #11
GGBGGB - possible position #12
GGGBBG - possible position #13
GGGBGB - possible position #14
GGGGBB - possible position #15

例如,他们选择位置GBBGGG ...现在我计算这个位置的数量(#6)的解决方案是循环所有可能的位置,并将它们中的每一个与选定的顺序进行比较并返回当前位置编号他们是平等的。

在上述示例的范围内,循环使用15种可能的组合并不是什么大不了的事,但如果增加椅子和人的范围,这种方法远没有效率。

是否有任何公式或更有效的方法可用于确定所选可能性的位置?您可以在示例中随意使用任何编程语言。

更新:我确切地知道房间里有多少椅子,男孩和女孩。唯一的问题是找到他们选择坐的可能性的位置数。

我在我的示例中使用的排序只是为了更好的可读性。欢迎任何类型的排序答案。

5 个答案:

答案 0 :(得分:7)

按G的位置查找排列的等级

示例中的排列位于lexicographical order;第一个排列的左边是所有的B,右边的是G;其他排列是通过逐渐向左移动G来实现的。 (类似于二进制数的上升序列:0011,0101,0110,1001,1010,1100)

要计算给定排列进入此过程的距离,请从左到右逐个查看字符:每当遇到G时,移动它所需的排列数(N选择K)其中N是当前位置右边的位置数,K是左边的G数,包括当前的G.

123456←职位
BBGGGG←排名0(或1)
BGBGGG←等级1(或2)
BGGBGG←排名2(或3)
BGGGBG←等级3(或4)
BGGGGB←等级4(或5)
GBBGGG←等级5(或6)
GBGBGG←等级6(或7)
GBGGBG←排名7(或8)

E.g。对于你的例子中的GBGGBG,在6个可能的位置有4个G,第一个G在位置1,所以我们计算(6-1选4)= 5;第二个G位于第3位,所以我们加(6-3选3)= 1;第三个G位于第4位,所以我们加(6-4选2)= 1;最后一个G位于第6位,因此它处于原始位置,可以忽略。这加起来为7,这意味着排列具有等级7(如果从1开始计数,则为8,就像在问题中一样)。

使用Pascal三角形计算(N选择K)

您可以使用例如Pascal's Triangle计算(N选择K)。这是一个三角形数组,其中每个数字是它上面两个数字的总和:

             K=0  K=1  K=2  K=3  K=4  K=5  K=6
      N=0    1
     N=1    1    1
    N=2    1    2    1
   N=3    1    3    3    1
  N=4    1    4    6    4    1
 N=5    1    5   10   10    5    1
N=6    1    6   15   20   15    6    1

代码示例

下面是一个简单的Javascript实现。运行代码段以查看一些示例。执行时间与椅子的数量成线性关系,而不是可能的排列数量,这可能是巨大的。 (更新:代码现在从右到左迭代字符,因此它不必先计算G的数量。)

function permutationRank(perm) {
    var chairs = perm.length, girls = 0, rank = 1;         // count permutations from 1
    var triangle = PascalsTriangle(chairs - 1);            // triangle[n][k] = (n choose k)
    for (var i = 1; i <= chairs; i++) {
        if (perm.charAt(chairs - i) == 'G' && ++girls < i) {
            rank += triangle[i - 1][girls];
        }
    }
    return rank;

    function PascalsTriangle(size) {
        var tri = [[1]];
        for (var n = 1; n <= size; n++) {
            tri[n] = [1];
            for (var k = 1; k < n; k++) {
                tri[n][k] = tri[n - 1][k - 1] + tri[n - 1][k];
            }
            tri[n][n] = 1;
        }
        return tri;
    }
}

document.write(permutationRank("BBGGGG") + "<BR>");
document.write(permutationRank("GBGGBG") + "<BR>");
document.write(permutationRank("GGGGBB") + "<BR>");
document.write(permutationRank("GGBGBBGBBBGBBBBGGGGGBBBBBGGGGBGGGBGGBGBB"));

反向算法:生成排列

该算法将执行相反的操作:给定B的数量,G的数量和置换的等级,它将返回置换。同样,这是在不必生成所有排列的情况下完成的。 (注意:我没有包括对输入有效性的任何检查)

function permutationGenerator(boys, girls, rank) {
    var chairs = boys + girls, perm = "";
    var triangle = PascalsTriangle(chairs - 1);  // triangle[n][k] = (n choose k)
    for (var i = chairs; i > 0; i--) {
        if (i > girls) {
            var choose = triangle[i - 1][girls];
            if (rank > choose) {                 // > if counting from 1, >= if counting from 0
                rank -= choose;
                perm += 'G';
                --girls;
            }
            else perm += 'B';
        }
        else perm += 'G';                        // only girls left
    }
    return perm;

    function PascalsTriangle(size) {
        var tri = [[1]];
        for (var n = 1; n <= size; n++) {
            tri[n] = [1];
            for (var k = 1; k < n; k++) {
                tri[n][k] = tri[n - 1][k - 1] + tri[n - 1][k];
            }
            tri[n][n] = 1;
        }
        return tri;
    }
}

document.write(permutationGenerator(2, 4, 1) + "<BR>");
document.write(permutationGenerator(2, 4, 8) + "<BR>");
document.write(permutationGenerator(2, 4, 15) + "<BR>");
document.write(permutationGenerator(20, 20, 114581417274));

答案 1 :(得分:3)

  

我的问题是找到有效的方法来计算他们选择坐的可能性的位置。欢迎任何类型的排序答案。是否有任何公式或更有效的方法可用于确定所选可能性的位置?

我会选择配置到二进制的映射:B1G0

对于7个男孩和3个女孩,有10!/(7! 3!) = 120组合,这里有一些组合位置:

GGGBBBBBBB <--> 0001111111
BBGBBGBBGB <--> 1101101101
BBBBBBBGGG <--> 1111111000

如果需要,您可以转换为十进制,但无论如何它都是一对一的映射,可以让您几乎立即确定位置。

答案 2 :(得分:2)

这是一种O(n)有效算法。没有帕斯卡三角形 - 它可以动态计算组合。 我已经测试过大值,生成组合并匹配排名,但如果你找到一个例子它不起作用,请告诉我。

http://dev-task.blogspot.com/2015/12/rank-of-n-bit-numbers-with-exactly-k.html

答案 3 :(得分:1)

我建议你使用二叉搜索树。每次添加一把椅子都会克隆到树的每一侧,而新的B或G选择将是唯一的区别。基本上,你克隆你拥有的东西,然后在旁边的每个条目中添加B或G.

编辑:请注意,这也可以用于定位的LogN搜索。

答案 4 :(得分:1)

分支定界(BB或B&amp; B)是离散和组合优化问题的算法设计范例,以及一般实值问题。分支定界算法由通过状态空间搜索的候选解决方案的系统枚举组成:候选解决方案集被认为是在根处形成具有完整集的有根树。该算法探索此树的分支,它代表解决方案集的子集。在枚举分支的候选解之前,将针对最优解的上下估计边界检查分支,并且如果它不能产生比迄今为止算法发现的最佳解更好的解,则丢弃该分支。

分支定界方法的本质是以下观察:在总枚举树中,在任何节点,如果我可以证明最佳解决方案不能在其任何后代中出现,那么就没有必要我要考虑那些后代节点。因此,我可以在该节点“修剪”树。如果我能以这种方式修剪树的足够分支,我可以将其减少到计算上可管理的大小。请注意,我并没有忽略我已修剪的分支叶子中的那些解决方案,在确保最佳解决方案不能位于这些节点中的任何一个之后,我将它们排除在外。因此,分支定界方法不是一种启发式或近似式过程,但它是一种精确的优化过程,可以找到最佳解决方案。