Find the nth power pack

I am trying to find the n-th set in a parameter set. By n-th I mean that a set of permissions is generated in the following order — first by size, and then, lexicographically — and, therefore, the indices of the sets in the force set [a, b, c] :

 0 - [] 1 - [a] 2 - [b] 3 - [c] 4 - [a, b] 5 - [a, c] 6 - [b, c] 7 - [a, b, c] 

When searching for a solution, all I could find was an algorithm to return the nth permutation of the list of elements - for example, here .

Context

I am trying to extract the entire set of elements of a vector of V elements, but I need to do this with one set at a time.

Requirements

  • I can only support two vectors at a time, the first with the original elements in the list, and the second with n-th set using the V parameter set - this is why I am ready for the n-th set function here:
  • I need this to be done not in linear time on the solution space, which means that he cannot list all the sets, and they select n-th one;
  • My initial idea is to use bits to represent positions and get the correct match for what I need - as the "incomplete" solution that I posted.
+4
source share
3 answers

I don't have a closed form for the function, but I have bit hacking not related to the next_combination , which you can use if that helps. It is assumed that you can put the bitmask in some integer type, which is probably not an unreasonable assumption, given that there are 2 64 for a 64-element set.

As the commentary says, I find this definition of “lexicographic ordering” a bit strange, since I would say that lexicographic ordering would be: [], [a], [ab], [abc], [ac], [b], [bc], [c] . But I had to do first "first by size, and then by lexicographic" listing.

 // Generate bitmaps representing all subsets of a set of k elements, // in order first by (ascending) subset size, and then lexicographically. // The elements correspond to the bits in increasing magnitude (so the // first element in lexicographic order corresponds to the 2^0 bit.) // // This function generates and returns the next bit-pattern, in circular order // (so that if the iteration is finished, it returns 0). // template<typename UnsignedInteger> UnsignedInteger next_combination(UnsignedInteger comb, UnsignedInteger mask) { UnsignedInteger last_one = comb & -comb; UnsignedInteger last_zero = (comb + last_one) &~ comb & mask; if (last_zero) return comb + last_one + (last_zero / (last_one * 2)) - 1; else if (last_one > 1) return mask / (last_one / 2); else return ~comb & 1; } 

Line 5 performs the bit-hacker equivalent of the (extended) regular expression replacement, which finds the last line 01 in the line, flips it to 10 and shifts all of the next 1 all right.

 s/01(1*)(0*)$/10\2\1/ 

Line 6 does this (only if the previous one failed) add another 1 and push 1 all the way to the right:

 s/(1*)0(0*)/\21\1/ 

I don't know if this explanation helps :)


Here's a quick and dirty driver (the command line argument is the set size, defaults to 5, the maximum number of bits in an unsigned long):

 #include <iostream> template<typename UnsignedInteger> std::ostream& show(std::ostream& out, UnsignedInteger comb) { out << '['; char a = 'a'; for (UnsignedInteger i = 1; comb; i *= 2, ++a) { if (i & comb) { out << a; comb -= i; } } return out << ']'; } int main(int argc, char** argv) { unsigned int n = 5; if (argc > 1) n = atoi(argv[1]); unsigned long mask = (1UL << n) - 1; unsigned long comb = 0; do { show(std::cout, comb) << std::endl; comb = next_combination(comb, mask); } while (comb); return 0; } 

It is hard to believe that this function can be useful for a collection of more than 64 elements, given the size of the enumeration, but it may be useful to list some limited part, such as all subsets of three elements. In this case, bit hackers are really useful if the modification fits into one word. Fortunately, this is easy to verify; you just need to perform the calculation, as indicated above, on the last word in the bitet, before the test for last_zero , which is zero. (In this case, you do not need to beat mask , and indeed, you can choose a different way of specifying the specified size.) If last_zero turns out to be zero (which will be really quite rare), then you need to do the conversion in some other way, but the principle the same: find the first 0 that precedes 1 (pay attention to the case when 0 is at the end of a word and 1 at the beginning of the next); change the value of 01 to 10 , find out how much 1 you need to move and move them to the end.

+4
source

Given the list of elements L = [a, b, c] , the set of parameters L is defined as follows:

 P(L) = { [], [a], [b], [c], [a, b], [a, c], [b, c], [a, b, c] } 

Given each position as a bit, you should have mappings:

 id | positions - integer | desired set 0 | [0 0 0] - 0 | [] 1 | [1 0 0] - 4 | [a] 2 | [0 1 0] - 2 | [b] 3 | [0 0 1] - 1 | [c] 4 | [1 1 0] - 6 | [a, b] 5 | [1 0 1] - 5 | [a, c] 6 | [0 1 1] - 3 | [b, c] 7 | [1 1 1] - 7 | [a, b, c] 

As you can see, id is not directly mapped to integers. You must apply the correct mapping so that you have:

 id | positions - integer | mapped - integer 0 | [0 0 0] - 0 | [0 0 0] - 0 1 | [1 0 0] - 4 | [0 0 1] - 1 2 | [0 1 0] - 2 | [0 1 0] - 2 3 | [0 0 1] - 1 | [0 1 1] - 3 4 | [1 1 0] - 6 | [1 0 0] - 4 5 | [1 0 1] - 5 | [1 0 1] - 5 6 | [0 1 1] - 3 | [1 1 0] - 6 7 | [1 1 1] - 7 | [1 1 1] - 7 

As an attempt to solve this, I came up with using a binary tree to display - I am sending it so that someone can see the solution from it:

  # ______________|_____________ a / \ _____|_____ _______|______ b / \ / \ __|__ __|__ __|__ __|__ c / \ / \ / \ / \ [ ] [c] [b] [b, c] [a] [a, c] [a, b] [a, b, c] index: 0 3 2 6 1 5 4 7 
+4
source

Suppose your set is size N.

So there are (N choose k) sets of size k. You can quickly find k (i.e. the size of the nth set) by simply subtracting (N select k) from n until n becomes negative. This reduces your problem to finding the nth k-subset of an N-set.

The first (N-1 selects k-1) k-subsets of your N-set will contain its smallest element. Thus, if n is less (N-1 selects k-1), select the first element and recursion on the rest of the set. Otherwise, you have one of (N-1 select k) other sets; throw away the first element, subtract (N-1 choose k-1) from n and recurse.

code:

 #include <stdio.h> int ch[88][88]; int choose(int n, int k) { if (n<0||k<0||k>n) return 0; if (!k||n==k) return 1; if (ch[n][k]) return ch[n][k]; return ch[n][k] = choose(n-1,k-1) + choose(n-1,k); } int nthkset(int N, int n, int k) { if (!n) return (1<<k)-1; if (choose(N-1,k-1) > n) return 1 | (nthkset(N-1,n,k-1) << 1); return nthkset(N-1,n-choose(N-1,k-1),k)<<1; } int nthset(int N, int n) { for (int k = 0; k <= N; k++) if (choose(N,k) > n) return nthkset(N,n,k); else n -= choose(N,k); return -1; // not enough subsets of [N]. } int main() { int N,n; scanf("%i %i", &N, &n); int a = nthset(N,n); for (int i=0;i<N;i++) printf("%i", !!(a&1<<i)); printf("\n"); } 
+2
source

All Articles