number to unique permutation mapping of a sequence containing duplicates

后端 未结 5 1133
野趣味
野趣味 2021-02-02 00:26

I am looking for an algorithm that can map a number to a unique permutation of a sequence. I have found out about Lehmer codes and the factorial number system thanks to a simila

5条回答
  •  走了就别回头了
    2021-02-02 01:31

    Here is an algorithm in Java that enumerates the possible sequences by mapping an integer to the sequence.

    public class Main {
    
        private int[] counts = { 3, 2, 1 }; // 3 Symbols A, 2 Symbols B, 1 Symbol C
        private int n = sum(counts);
    
        public static void main(String[] args) {
            new Main().enumerate();
        }
    
        private void enumerate() {
            int s = size(counts);
            for (int i = 0; i < s; ++i) {
                String p = perm(i);
                System.out.printf("%4d -> %s\n", i, p);
            }
    
        }
    
        // calculates the total number of symbols still to be placed
        private int sum(int[] counts) {
            int n = 0;
            for (int i = 0; i < counts.length; i++) {
                n += counts[i];
            }
            return n;
        }
    
        // calculates the number of different sequences with the symbol configuration in counts
        private int size(int[] counts) {
            int res = 1;
            int num = 0;
            for (int pos = 0; pos < counts.length; pos++) {
                for (int den = 1; den <= counts[pos]; den++) {
                    res *= ++num;
                    res /= den;
                }
            }
            return res;
        }
    
        // maps the sequence number to a sequence
        private String perm(int num) {
            int[] counts = this.counts.clone();
            StringBuilder sb = new StringBuilder(n);
            for (int i = 0; i < n; ++i) {
                int p = 0;
                for (;;) {
                    while (counts[p] == 0) {
                        p++;
                    }
                    counts[p]--;
                    int c = size(counts);
                    if (c > num) {
                        sb.append((char) ('A' + p));
                        break;
                    }
                    counts[p]++;
                    num -= c;
                    p++;
                }
            }
            return sb.toString();
        }
    
    }
    

    The mapping used by the algorithm is as follows. I use the example given in the question (3 x A, 2 x B, 1 x C) to illustrate it.

    There are 60 (=6!/3!/2!/1!) possible sequences in total, 30 (=5!/2!/2!/1!) of them have an A at the first place, 20 (=5!/3!/1!/1!) have a B at the first place, and 10 (=5!/3!/2!/0!) have a C at the first place.

    The numbers 0..29 are mapped to all sequences starting with an A, 30..49 are mapped to the sequences starting with B, and 50..59 are mapped to the sequences starting with C.

    The same process is repeated for the next place in the sequence, for example if we take the sequences starting with B we have now to map numbers 0 (=30-30) .. 19 (=49-30) to the sequences with configuration (3 x A, 1 x B, 1 x C)

提交回复
热议问题