9

For example there are 6 chairs in the room and there are 4 girls and 2 boys. There is 15 unique possible ways they can sit on this chairs 6!/(4!*2!)=15.

My problem is to find efficient way to calculate position of possibility they choose to sit. By position I mean following:

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

For example they choose position GBBGGG... For now my solution to calculate number of this position (#6) is to loop all possible positions and compare each of them to selected order and return current position number if they are equal.

In this range from example above, it's not a big deal to loop in 15 possible combinations, but if you increase range of chairs and people, this method is way far from efficient.

Is there any formula or more efficient way I can use to determinate position of selected possibility? Feel free to use any programming language in your examples.

UPDATE: I know exactly how many chairs, boys and girls are in the room. Only problem is to find position number of possibility they choose to sit.

Sorting I'm using in my example is for better readability only. Answers with any type of sorting are welcome.

Nicolo
  • 1,600
  • 2
  • 16
  • 18
  • Are you guaranteed that the chairs will start at a minimum lexicographical representation each iteration iteration will increase the representation by 1? – NathanOliver Dec 11 '15 at 18:35
  • @NathanOliver Yes, I can guarantee that but I also can change way how I increase positions if there is more efficient way to calculate position of selected combination. – Nicolo Dec 11 '15 at 18:41
  • If you find an answer for this, I'd love to see it. I wanted the same thing for a blackjack strategy optimiser some years ago. – Richard Hodges Dec 11 '15 at 18:53
  • Your position numbers are arbitrary. I don't understand what output you want to get. If I give you GGBBBGB for 7 people, what number do you expect? – user1803551 Dec 11 '15 at 19:10
  • @user1803551 No, I know exactly how many chairs, boys and girls are in the room. I will edit question to make it more clear. – Nicolo Dec 11 '15 at 19:13
  • 2
    So, to clarify, are you looking for an algorithm that will 1. provide all possible combinations of seating arrangments 2. verify position order from the combinations you have generated? – Michael Queue Dec 11 '15 at 19:13
  • I understand that the number of boys and girls is known and constant, I don't understand how you order the positions. How do you get from GGGBBG to 13? – user1803551 Dec 11 '15 at 19:16
  • I believe the OP is sorting them lexically. – rajah9 Dec 11 '15 at 19:25
  • @MichaelQueue If you mean generating list of all possible combinations first, it can work on small ranges... But if there is any way to calculate unique position of selected possibility without generating list, it will be better. – Nicolo Dec 11 '15 at 19:25
  • @rajah9 You believe, but the OP does not say anything about it, still waiting for an answer. – user1803551 Dec 11 '15 at 19:28
  • @user1803551 Sorting in my example is just example. I don't care how it will be sorted if I can get unique position of selected possibility. – Nicolo Dec 11 '15 at 19:29
  • But you want a position without generating a list, so what defines the position? Answer this: if you have 2 Bs and 3Gs, what number position is BGBGG? – user1803551 Dec 11 '15 at 19:30
  • @user1803551 that will be second position – vsoftco Dec 11 '15 at 19:30
  • @vsoftco Which sentences in the OP's post lead you to that conclusion? – user1803551 Dec 11 '15 at 19:31
  • @user1803551 I believe OP is generating new position by moving the right-hand chair one position to the right, up to the end, then repeat (decreasing the position of the lefthand chair by one and resetting the system). – vsoftco Dec 11 '15 at 19:32
  • @vsoftco You believe, but the OP does not say anything about it, still waiting for an answer. – user1803551 Dec 11 '15 at 19:33
  • @user1803551 At least that's a bit consistent with how OP defined the positions in her/his post. – vsoftco Dec 11 '15 at 19:34
  • I think it may be better to say that you want an explicit bijection between the configuration space and 1...N (or 0 to N-1 so the CS people don't get mad), where `N` is the maximum number of configurations (given by the combinatorial formula). – vsoftco Dec 11 '15 at 19:38
  • @user1803551 I think sorting is part of the problem as well when possible combinations can hit millions if I increase quantity of chairs. But any answer is welcome either if it's using list I already have or with other sorting types or ways how to get unique position number without list. – Nicolo Dec 11 '15 at 19:42
  • 1
    It sounds like you might be looking for an algorithm to "rank permutations with duplicates." – גלעד ברקן Dec 11 '15 at 20:26
  • @RichardHodges check best answer, this is exactly what I wanted and hope you find it useful too! :) – Nicolo Dec 12 '15 at 21:46
  • @Nicolo I edited the title to give the problem its mathematical name, but maybe I should have put "lexicographical" between brackets, since you were open to answers that used other orderings? – m69's been on strike for years Dec 12 '15 at 22:39
  • @Nicolo thanks for remembering. I appreciate it. – Richard Hodges Dec 12 '15 at 22:39
  • @m69 Thanks and no need brackets, looks good. – Nicolo Dec 12 '15 at 22:42

5 Answers5

8

Finding the rank of a permutation by position of G's

The permutations in the example are in lexicographical order; the first permutation has all the B's on the left and the G's on the right; the other permutations are made by gradually moving G's to the left. (Similar to a rising sequence of binary numbers: 0011, 0101, 0110, 1001, 1010, 1100)

To count how far into this process a given permutation is, look at the characters one by one from left to right: whenever you encounter a G, the number of permutations needed to move it there is (N choose K) where N is the number of positions to the right of the current position, and K is the number of G's left, including the current G.

123456 ← positions
BBGGGG ← rank 0 (or 1)
BGBGGG ← rank 1 (or 2)
BGGBGG ← rank 2 (or 3)
BGGGBG ← rank 3 (or 4)
BGGGGB ← rank 4 (or 5)
GBBGGG ← rank 5 (or 6)
GBGBGG ← rank 6 (or 7)
GBGGBG ← rank 7 (or 8)

E.g. for GBGGBG in your example, there are 4 G's in 6 possible positions, and the first G is at position 1, so we count (6-1 choose 4) = 5; the second G is at position 3, so we add (6-3 choose 3) = 1; the third G is at position 4, so we add (6-4 choose 2) = 1; the last G is at position 6, so it's in its original position and can be ignored. This adds up to 7, which means the permutation has rank 7 (or 8 if you start counting from 1, like you do in the question).

Calculating (N choose K) with Pascal's Triangle

You can use e.g. Pascal's Triangle to calculate (N choose K). This is a triangular array where each number is the sum of the two numbers above it:

             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

Code example

Below is a simple Javascript implementation. Run the code snippet to see a few examples. The execution time is linear to the number of chairs, not to the number of possible permutations, which could be huge. (update: the code now iterates over the characters from right-to-left, so that it doesn't have to count the number of G's first.)

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"));

Inverse algorithm: generate permutation

This algorithm will do the inverse: given the number of B's, the number of G's, and the rank of the permutation, it will return the permutation. Again, this is done without having to generate all the permutations. (note: I have not included any checking of the validity of the input)

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));
  • Thanks! Looks good and I will test it later for accuracy but what about reverse operation? For example when I run your code with `GBGGBG` I got #8 and how can I translate it back to `GBGGBG`? – Nicolo Dec 12 '15 at 13:51
  • @Nicolo the reverse operation is a different question, of course. However, I'm sure you can invert the (n choose k) method to calculate this too. Create the triangle, start from the bottom up, find the biggest numbers that add up to the permutation number, then move the G's into place accordingly. – m69's been on strike for years Dec 12 '15 at 15:02
  • Got it! :) I will finish writing reverse function soon. Is it ok to edit your answer when I'm done (I will only add reverse function snippet)? Since your answer is marked as best answer, other people can find reverse function useful and it's better if I add it to best answer instead of answering my own question with another answer. Thanks again! – Nicolo Dec 12 '15 at 17:11
  • @Nicolo I was going to have a look at it myself later today. Feel free to add it if you get there first :-) – m69's been on strike for years Dec 12 '15 at 20:32
  • Hah! I was going to edit your post and got warning about post already have been edited :) Anyway, check my modification I was going to post http://jsbin.com/cucirerada/1/edit?js,console – Nicolo Dec 12 '15 at 21:42
  • @Nicolo Well, it appears we're doing pretty much the same thing, so it must be right :-) – m69's been on strike for years Dec 12 '15 at 22:05
  • @Nicolo After looking at Avik Paul's answer, I realised that going right-to-left makes more sense, because it simplifies the code. I edited the answer with updated code. – m69's been on strike for years Dec 15 '15 at 00:56
3

My problem is to find efficient way to calculate position of possibility they choose to sit. Answers with any type of sorting are welcome. Is there any formula or more efficient way I can use to determinate position of selected possibility?

I will pick the mapping of configuration to binary: B is 1 and G is 0.

For 7 boys and 3 girls there are 10!/(7! 3!) = 120 combinations, here are some positions of combinations:

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

You can convert to decimal if you need to, but in any case it's a 1 to 1 mapping which allows you to determine the position almost immediately.

user1803551
  • 12,965
  • 5
  • 47
  • 74
  • This is incorrect, because you're counting every binary number, instead of only those that contain the right number of 0's and 1's. Look at the examples in the question. – m69's been on strike for years Dec 12 '15 at 01:32
  • @m69 You are wrong, I don't count the binary number `1111111111`, for example. In the example in the question i would count only binary numbers with 2 `1` and 4 `0`. – user1803551 Dec 12 '15 at 01:44
  • So how do you determine the position almost immediately? – m69's been on strike for years Dec 12 '15 at 01:47
  • @m69 The position is the binary position. By the simple substitution you determine it almost immediately. I think that you are adding assumptions that the OP didn't state. – user1803551 Dec 12 '15 at 01:49
  • @m69 Alternatively, I can ask you which parts of the OP's question contradict the validity of my solution. – user1803551 Dec 12 '15 at 01:55
  • Let me re-phrase: everything you write is correct, but your solution only transforms the problem into another problem that isn't easier to solve. How do you propose to transform `BBGBBGBBGB` or `1101101101` into the desired result (which is 43) without iterating over every permutation? – m69's been on strike for years Dec 12 '15 at 05:40
  • @user1803551 (please correct me if I'm wrong) I think is proposing to create a symbol table (or hashmap) so that lookup time will be O(1). However, as OP implied or alluded to or whatever "If you mean generating list of all possible combinations first, it can work on small ranges... " This problem needs to be decomposed into two problems. However, that being said, this is a great solution for the 2nd part of the problem. – Michael Queue Dec 12 '15 at 07:31
  • @m69 *How do you propose to transform `BBGBBGBBGB` or `1101101101` into the desired result (which is 43)* Here is where you made the assumptions (like 2 others in the question's comment section) that the ordering is lexical. The OP addressed it and clearly stated that the *Sorting I'm using in my example is for better readability only. Answers with any type of sorting are welcome*. So 43 is the result in *your* mapping.You made a choice to start with `B`s on the left and call it `1`, why not start of them in the middle and call it `1`? The mapping is arbitrary. – user1803551 Dec 12 '15 at 13:41
  • @MichaelQueue Not exactly. I don't need to create a map or a list with all combinations. You can give me any configuration of B's and G's and I will tell you the position without looking up any data structure. – user1803551 Dec 12 '15 at 13:49
  • Well, the OP isn't exactly helping by being a bit vague and contradictory at times, of course. Still, I'm not convinced that this "almost immediate" translation actually works. – m69's been on strike for years Dec 12 '15 at 14:56
  • From the question, "By position I mean following:" shows what the OP means by "position". It is not the sex of the occupant of each chair, it is the a single valid configuration of boys and girls in the N chairs. "There is a girl in the first seat" is not a "position". "BBGGGG" **is** a "position" (as defined within this question). – beaker Dec 12 '15 at 15:48
  • @beaker Read the OP's edit: *Sorting I'm using in my example is for better readability only. Answers with any type of sorting are welcome*. The positions I'm giving are numbers, choose which representation you want for them and convert. Also "the sex of the occupant of each chair [where there are N chairs]" is exactly the same as "a single valid configuration of boys and girls in the N chairs". No where do I use "There is a girl in the first seat" as a position, I always use the whole configuration to reach the position. – user1803551 Dec 12 '15 at 16:03
  • Then I fail to see how you are mapping the indices `1..(n choose k)` to valid configurations in any ordering. – beaker Dec 12 '15 at 16:05
  • @beaker If you are talking about the reverse mapping then it is done the way I posted, not with numbers in the `n choose k` range. `n choose k` is just the number of configurations, the positions don't have to correspond to numbers in that range. For example, take the example in the OP's question and multiply all positions by 2. It is still a valid sorted 1 to 1 mapping; it doesn't matter that position #22 is larger than 15. – user1803551 Dec 12 '15 at 16:16
  • @user1803551 thanks for your answer and I'm sure other people will find it useful too. In my case other answer was more useful but I upvoted your answer as well. – Nicolo Dec 12 '15 at 17:21
2

Branch and bound (BB or B&B) is an algorithm design paradigm for discrete and combinatorial optimization problems, as well as general real valued problems. A branch-and-bound algorithm consists of a systematic enumeration of candidate solutions by means of state space search: the set of candidate solutions is thought of as forming a rooted tree with the full set at the root. The algorithm explores branches of this tree, which represent subsets of the solution set. Before enumerating the candidate solutions of a branch, the branch is checked against upper and lower estimated bounds on the optimal solution, and is discarded if it cannot produce a better solution than the best one found so far by the algorithm.

The essence of the branch-and-bound approach is the following observation: in the total enumeration tree, at any node, if I can show that the optimal solution cannot occur in any of its descendents, then there is no need for me to consider those descendent nodes. Hence, I can "prune" the tree at that node. If I can prune enough branches of the tree in this way, I may be able to reduce it to a computationally manageable size. Note that, I am not ignoring those solutions in the leaves of the branches that I have pruned, I have left them out of consideration after I have made sure that the optimal solution cannot be at any one of these nodes. Thus, the branch-and-bound approach is not a heuristic, or approximating, procedure, but it is an exact, optimizing procedure that finds an optimal solution.

Michael Queue
  • 1,340
  • 3
  • 23
  • 38
2

Here is an O(n) efficient algorithm. No pascals triangle - it computes the combinations on the fly. I have tested against large values, generating the combinations and matching the ranks, yet if you find an example it does not work, let me know.

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

Avik Paul
  • 76
  • 4
  • Thanks. Looks like it can increase performance, but main performance issue is in reverse operation. Is it possible to run reverse operation on-the-fly as well? – Nicolo Dec 13 '15 at 09:47
  • You are right, in reverse operation the only option is to first subtract the large combination value to find the remainder, so it should start with the large combination value C(n-1,k). I think once you have computed C(n-1,k), the smaller combinations can be derived iteratively on-the-fly, based on how n and k change. But the value C(n-1,k) should be available though. – Avik Paul Dec 13 '15 at 10:01
  • 2
    Interesting. Btw, can you describe the main ideas of the answer here, instead of just linking to it? This is preferred on SO. – m69's been on strike for years Dec 13 '15 at 16:35
1

I would recommend you use a binary search tree. Every time you add a chair each side of the tree will be cloned and the new choice of either B or G will be the only difference. Basically, you clone what you have and then add B or G to each entry on the side.

EDIT : Note that this can be used for a LogN search of the positioning as well.

soulsabr
  • 895
  • 4
  • 16