Lexicographic Rank of a Set Partitioned Into Groups - c++

Given a set of 8 sequential numbers {0..7} partitioned into 4 groups of size 2, with the numbers in each group in ascending order, how can a rank be generated for the set? The rank should be in lexicographic order, and preferably the algorithm should be linear in complexity.
Examples of the partitioning:
{{0 1} {2 3} {4 5} {6 7}} // Rank 0
...
{{6 7} {4 5} {2 3} {0 1}} // Rank 2519
Because the numbers in each group are in ascending order, the groups are effectively treated like combinations, not permutations, so a group containing e.g. {5 4} will never occur.
How can this set of numbers be ranked sequentially in the range [0, 2520) (8C2 * 6C2 * 4C2)?
At present I compute the rank of each group as an 8C2 combination, then combine each rank together by treating it as a base-28 number. This obviously leaves gaps in the ranking, which is undesirable in my case. But, for what it's worth, here is how I'm currently ranking.
#include <array>
using std::array;
#include <cstdint>
#include <cstddef>
#include <iostream>
using std::cout;
using std::endl;
// Calculates n!.
uint32_t factorial(uint32_t n)
{
return n <= 1 ? 1 : n * factorial(n - 1);
}
// Calculate nCk: n!/((n-k)!*k!).
uint32_t choose(uint32_t n, uint32_t k)
{
return (n < k)
? 0
: factorial(n) / (factorial(n - k) * factorial(k));
}
template<size_t N, size_t K>
class CombinationRanker
{
array<array<uint32_t, K+1>, N+1> choices;
public:
/**
* Initialize a precomputed array of nCk (N and K inclusive).
*/
CombinationRanker()
{
for (unsigned n = 0; n <= N; ++n)
for (unsigned k = 0; k <= K; ++k)
this->choices[n][k] = choose(n, k);
}
/**
* Get the rank of a combination.
* #param comb A combination array of size K in ascending order.
*/
uint32_t rank(const array<uint8_t, K> comb) const
{
// Formula: (nCk) - ((n-c_1)Ck) - ((n-c_2)C(k-1)) - ... - ((n-c_k)C1)
// That assumes 1-based combinations with ranks starting at 1, so each
// element in the combination has 1 added to it, and the end result has 1
// subtracted from it to make the rank 0-based.
uint32_t rank = this->choices[N][K];
for (unsigned i = 0; i < K; ++i)
rank -= this->choices[N - (comb[i] + 1)][K - i];
return rank - 1;
}
};
int main(int argc, char* argv[])
{
CombinationRanker<8, 2> ranker;
array<array<uint8_t, 2>, 4> nums =
{{
{0, 1}, {2, 3}, {4, 5}, {6, 7}
}};
// Horribly sparse rank.
unsigned rank =
ranker.rank(nums[0]) * 28 * 28 * 28 +
ranker.rank(nums[1]) * 28 * 28 +
ranker.rank(nums[2]) * 28 +
ranker.rank(nums[3]);
cout << rank << endl; // 10835, but I want 0.
return 0;
}
I've tagged the post as C++ as that's the language I'm using; however, answers in another language are fine. It's more of a math question, but I'm looking for an answer that I can understand as a programmer, not a mathematician, and a code snippet would be helpful in that regard.

Here's what I came up with. It's quadratic in complexity, which is not the greatest, but it does the trick. The basic algorithm is as follows.
Given a set of sequential numbers from [0..7] partitioned into unordered pairs, loop over each pair and find its rank among pairs that
exclude numbers preceding it. Then multiplying each rank by its variable
base. The variable bases for each rank are 6C2*4C2*2C2, 4C2*2C2, and 2C2.
As an example, for {{2,3}, {6,7}, {4,5}, {0,1}}:
{2, 3} has rank 13.
{6, 7} has rank 14 among pairs excluding 2 and 3.
{4, 5} has rank 5 among pairs excluding 2, 3, 6, and 7.
{0, 1} is ignored.
Altogether, 13*6C2*4C2*2C2 + 14*4C2*2C2 + 5*2C2 = 1259
Other examples:
{{0, 1}, {2, 3}, {4, 5}, {6, 7}} -> 0
{{2, 3}, {6, 7}, {4, 5}, {0, 1}} -> 1259
{{2, 4}, {0, 1}, {3, 5}, {6, 7}} -> 1260
{{6, 7}, {4, 5}, {2, 3}, {0, 1}} -> 2519
Here's algorithm in code. I've hard coded quite a bit for brevity.
#include <iostream>
using std::cout;
using std::endl;
#include <array>
using std::array;
#include <cstdint>
typedef array<uint8_t, 2> pair_t;
/**
* #param set A set of 8 sequential numbers, [0..7], partitioned into unordered
* pairs.
*/
uint32_t rank(const array<pair_t, 4>& set) {
// All 28 (8C2) possible unordered subsets of the set of 8 sequential
// numbers, [0..7], in lexicographic order. Hard-coded here for brevity.
array<pair_t, 28> pairs = {{
{0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5}, {0, 6}, {0, 7},
{1, 2}, {1, 3}, {1, 4}, {1, 5}, {1, 6}, {1, 7},
{2, 3}, {2, 4}, {2, 5}, {2, 6}, {2, 7},
{3, 4}, {3, 5}, {3, 6}, {3, 7},
{4, 5}, {4, 6}, {4, 7},
{5, 6}, {5, 7},
{6, 7},
}};
// Variable base for each rank "digit" (the base corresponding to the rank of
// each subset): 6C2*4C2*2C2, 4C2*2C2, 2C2. Again, hard-coded for brevity.
array<uint32_t, 3> bases = {{90, 6, 1}};
// Now rank the set.
uint32_t rank = 0;
// Rank among this many pairs. For N=8, 8C2->6C2->4C2->2C2 (28->15->6->1).
unsigned numRemaining = 28; // N*(N-1)/2
array<pair_t, 28> remaining = pairs;
// Loop over the first three unordered subsets. The last isn't needed for
// ranking--n from [0...(N-2)/2).
for (unsigned n = 0; n < 3; ++n)
{
unsigned remainingInd = 0;
const pair_t& sPair = set[n];
for (unsigned r = 0; r < numRemaining; ++r)
{
const pair_t& rPair = remaining[r];
if (sPair == rPair)
{
// Found the pair: rank it relative to the ramining pairs, and multiply
// it by the base for digit n.
rank += r * bases[n];
}
else if (
sPair[0] != rPair[0] && sPair[0] != rPair[1] &&
sPair[1] != rPair[0] && sPair[1] != rPair[1]
)
{
// The pair excludes the numbers in set[n], so keep it in the
// list of remaining pairs for the next digit's rank.
remaining[remainingInd++] = rPair;
}
}
// Number of remaining pairs.
numRemaining = remainingInd;
}
return rank;
}
int main(int argc, char* argv[])
{
// Examples pairs.
array<array<pair_t, 4>, 7> sets = {{
{{{0, 1}, {2, 3}, {4, 5}, {6, 7}}},
{{{0, 1}, {2, 3}, {4, 6}, {5, 7}}},
{{{0, 1}, {2, 3}, {4, 7}, {5, 6}}},
{{{0, 1}, {2, 3}, {5, 6}, {4, 7}}},
// snip
{{{2, 3}, {6, 7}, {4, 5}, {0, 1}}},
// snip
{{{6, 7}, {4, 5}, {1, 3}, {0, 2}}},
{{{6, 7}, {4, 5}, {2, 3}, {0, 1}}},
}};
for (unsigned i = 0; i < 7; ++i)
{
const array<pair_t, 4>& set = sets[i];
cout << rank(set) << ": ";
for (unsigned j = 0; j < 4; ++j)
cout << '{' << (unsigned)set[j][0] << ", " << (unsigned)set[j][1] << '}';
cout << endl;
}
return 0;
}
Output:
0: {0, 1}{2, 3}{4, 5}{6, 7}
1: {0, 1}{2, 3}{4, 6}{5, 7}
2: {0, 1}{2, 3}{4, 7}{5, 6}
3: {0, 1}{2, 3}{5, 6}{4, 7}
1259: {2, 3}{6, 7}{4, 5}{0, 1}
2518: {6, 7}{4, 5}{1, 3}{0, 2}
2519: {6, 7}{4, 5}{2, 3}{0, 1}

Related

generating a set of sets that appear in every set

I have an array of arrays of things
typedef std::vector<thing> group;
std::vector<group> groups;
things could be compared like so
int comparison(thing a, thing b);
where the return value is 0, 1 or 2
0 means that the things are not alike
1 means that they are alike and a is more specific or equal to b
2 means that they are alike and b is more specific or equal to a
and I am looking for a function that would return me a group that contains all things that appear in every group.
std::getgroup(groups.begin(), groups.end(), myComparisonFunction);
the problem is I have no idea what this function may be called, if it does even exist, or what the closest thing to it would be.
Eventually, what you want is an intersection. Luckily, there is std::set_intersection which almost does what you need. Here's a simple example on std::vector<std::vector<int>>. You can easily change it to work with your thing:
#include <iostream>
#include <vector>
#include <algorithm>
std::vector<int> getGroup(const std::vector<std::vector<int>>& groups) {
std::vector<int> group;
std::vector<int> temp = groups[0];
std::sort(temp.begin(), temp.end());
for ( unsigned i = 1; i < groups.size(); ++i ) {
group = std::vector<int>();
std::vector<int> temp2 = groups[i];
std::sort(temp2.begin(), temp2.end());
std::set_intersection(temp2.begin(), temp2.end(),
temp.begin(), temp.end(),
std::back_inserter(group));
temp = group;
}
return group;
}
int main() {
std::vector<std::vector<int>> groups = { {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
{1, 2, 3, 5, 6, 7, 8, 10},
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
{1, 3, 4, 5, 6, 9, 10},
{1, 2, 6, 7, 8, 9, 10},
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} };
for ( auto g : getGroup(groups) )
std::cout << g << "\n";
return 0;
}
This will print:
1
6
10

Generate all possible ordered subset from a set

I'm aware how to generate all possible subsets from a set incorporating bit twiddling. For instance,
//Get if nth position's bit is set
bool IsBitSet(int num, int bit)
{
return 1 == ((num >> bit) & 1);
}
int subsetMaxIterCount = pow(2, someList.size());
for (int i = 0; i < subsetMaxIterCount; i++) {
vector<A> subset;
for (size_t i = 0; i < jobList.size(); i++)
{
if (IsBitSet(jobSubsetIdx, i)) {
//Add to subset here
}
}
//Here we have a subset for some i
}
However, this doesn't take into account of ordering.
For instance, if I had a set of {1, 2, 3}, the above algorithm generates subsets of:
{}, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1,2,3}
What I need in reality is this
{}, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1,2,3}, {2, 1}, {2, 1, 3}, {2, 3, 1}, {3, 1}, {3, 2}, {3, 1, 2}, {3, 2, 1}
Not sure if the above list is exhaustive. What's an effective algorithm in generating something like this? (Is this all possible subsets with permutation by the way?)
The way we generate the subsets using bit twiddling, every subset is sorted within it e.g. {1, 2, 3}, {2, 3}, {1, 3}. You can generate permutation for each subset using next_permutation
vector<vector<int>> mySubsetGenerator(vector<vector<int>>& subsets) {
vector<vector<int>> extendedSubset;
for(int i = 0; i < subsets.size(); ++i) {
do {
extendedSubset.push_back(subsets[i]);
} while(next_permutation(subsets[i].begin(), subsets[i].end()));
}
return extendedSubset;
}
Moreover, you can use only backtracking to generate all possible permutations by taking one or more elements of array.

Having some trouble with initializing a 2D array and then sending it off to a function

I am making a TicTacToe program and I'm trying to use OOP techniques. Within my 'Board' class I am wanting the program to store each way a set of moves can be won.
I hope this can be demonstrated here:
Board.h
#pragma once
class Board
{
private:
int winningRows[8][3]; //Variable in question
public:
static const char X = 'X'; //Game piece 'X'
static const char O = 'O'; //Game piece 'O'
static const char EMPTY = ' '; //Empty game piece
static const char TIE = 'T'; //Game is tie
static const char NOONE = 'N'; //Nobody has won game yet
static const int numbOfSquares = 9; //Number of squares on the board
int InitializeWinningCombinations();
void FindWinner();
};
Board.cpp
#include "stdafx.h"
#include "Board.h"
int Board::InitializeWinningCombinations()
{
/*
The playing board
0, 1, 2
3, 4, 5
6, 7, 8
*/
//All possible ways player can win game
winningRows[8][3] = {
//Horizontal
{0, 1, 2},
{3, 4, 5},
{6, 7, 8},
//Vertical
{0, 3, 6},
{1, 4, 7},
{2, 5, 8},
//Diagonal
{2, 4, 6},
{0, 4, 8}
};
//return winnigRows[8][3];
}
void Board::FindWinner()
{
//I am wanting to get the variable here so I can play around with it later.
int winningRows = InitializeWinningCombinations();
}
I could just have the 'winningRows' variable inside the 'FindWinnner' function but from my understanding it is best to abstract as much as possible and have it as a member of the 'Board' class
Thank you for your time.
winningRows[8][3] = {
//Horizontal
{0, 1, 2},
{3, 4, 5},
{6, 7, 8},
//Vertical
{0, 3, 6},
{1, 4, 7},
{2, 5, 8},
//Diagonal
{2, 4, 6},
{0, 4, 8}
};
Is an attempted array assignment not an initialization and it cannot be done. You can initialize the array in a constructor like
Board() : winningRows{
//Horizontal
{0, 1, 2},
{3, 4, 5},
{6, 7, 8},
//Vertical
{0, 3, 6},
{1, 4, 7},
{2, 5, 8},
//Diagonal
{2, 4, 6},
{0, 4, 8}
} {}
Live Example
You'd have to change the signature to
int** InitializeWinningCombinations();
Then you could call it as
int** winningRows = InitializeWinningCombinations();

Iteratively calculate the power set of a set or vector

While there are plenty of examples on how to generate the actual power set of a set, I can't find anything about iteratively (as in std::iterator) generating the power set. The reason why I would appreciate such an algorithm is the size of my base set. As the power set of a n-element set has 2^n elements, I would quickly run out of memory when actually computing the set. So, is there any way to create an iterator for the power set of a given set? Is it even possible?
If it would be easier, an iterator that creates sets of ints would be fine - I could use them as indices for the actual set/vector.
As I actually work on a std::vector, random access would be possible if neccessary
Using for_each_combination from Combinations and Permutations one can easily iterate through all members of the power set of a std::vector<AnyType>. For example:
#include <vector>
#include <iostream>
#include "../combinations/combinations"
int
main()
{
std::vector<int> v{1, 2, 3, 4, 5};
std::size_t num_visits = 0;
for (std::size_t k = 0; k <= v.size(); ++k)
for_each_combination(v.begin(), v.begin()+k, v.end(),
[&](auto first, auto last)
{
std::cout << '{';
if (first != last)
{
std::cout << *first;
for (++first; first != last; ++first)
std::cout << ", " << *first;
}
std::cout << "}\n";
++num_visits;
return false;
});
std::cout << "num_visits = " << num_visits << '\n';
}
This visits each power set member of this vector, and executes the functor, which simply counts the number of visits and prints out the current power set:
{}
{1}
{2}
{3}
{4}
{5}
{1, 2}
{1, 3}
{1, 4}
{1, 5}
{2, 3}
{2, 4}
{2, 5}
{3, 4}
{3, 5}
{4, 5}
{1, 2, 3}
{1, 2, 4}
{1, 2, 5}
{1, 3, 4}
{1, 3, 5}
{1, 4, 5}
{2, 3, 4}
{2, 3, 5}
{2, 4, 5}
{3, 4, 5}
{1, 2, 3, 4}
{1, 2, 3, 5}
{1, 2, 4, 5}
{1, 3, 4, 5}
{2, 3, 4, 5}
{1, 2, 3, 4, 5}
num_visits = 32
The syntax I've used above is C++14. If you have C++11, you will need to change:
[&](auto first, auto last)
to:
[&](std::vector<int>::const_iterator first, std::vector<int>::const_iterator last)
And if you are in C++98/03, you will have to write a functor or function to replace the lambda.
The for_each_combination function allocates no extra storage. This is all done by swapping members of the vector into the range [v.begin(), v.begin()+k). At the end of each call to for_each_combination the vector is left in its original state.
If for some reason you want to "exit" the for_each_combination early, simply return true instead of false.

2D int array in C++

So I want to initialize an int 2d array very quickly, but I can't figure out how to do it. I've done a few searches and none of them say how to initialize a 2D array, except to do:
int [SOME_CONSTANT][ANOTHER_CONSTANT] = {{0}};
Basically, I've got 8 vertices, and I'm listing the 4 vertices of each face of a cube in an array. I've tried this:
int[6][4] sides = {{0, 1, 2, 3}, {4, 5, 6, 7}, {0, 4, 7, 3}, {7, 6, 2, 3}, {5, 1, 2, 6}, {0, 1, 5, 4}};
But that tells me that there's an error with 'sides', and that it expected a semi-colon. Is there any way to initialize an array quickly like this?
Thanks!
You have the [][] on the wrong side. Try this:
int sides[6][4] = {{0, 1, 2, 3}, {4, 5, 6, 7}, {0, 4, 7, 3}, {7, 6, 2, 3}, {5, 1, 2, 6}, {0, 1, 5, 4}};
Keep in mind that what you really have is:
int **sides
(A pointer to a pointer of ints). It's sides that has the dimensions, not the int. Therefore, you could also do:
int x, y[2], z[3][4], ...;
I think You meant to say
int sides[6][4] = {{0, 1, 2, 3}, {4, 5, 6, 7}, {0, 4, 7, 3}, {7, 6, 2, 3}, {5, 1, 2, 6}, {0, 1, 5, 4}};
int array[n][m] behaves just like int array[n * m].
In fact, array[i][j] = array[m * i + j] for all i, j.
So int array[2][3] = {1, 2, 3, 4, 5, 6}; is a valid declaration and, for example,
array[1][1] = array[3 * 1 + 1] = array[4] = 5.
int sides[6][4] = {{0, 1, 2, 3}, {4, 5, 6, 7}, {0, 4, 7, 3}, {7, 6, 2, 3}, {5, 1, 2, 6}, {0, 1, 5, 4}};
I'm not a regular c++ programmer but I looks like int sides[6][4] seems to compile while int[6][4] sides fails. Languages like C# lets you have the [][] on either sides but apparently c++ doesn't.
int sides[6][4] = ... should do the trick. This sounds like you may be coming from a Java (or other language) background so I do recommend a C++ book The Definitive C++ Book Guide and List for more details.
Yes, the intended type of sides is int[6][4], but C++ has confusing syntax sometimes. The way to declare said array is:
int sides[6][4] = {/*stuff*/};
You run into this with function pointers too, but even worse:
int (*myfuncptr)(int); //creates a function pointer called myfuncptr
With function pointers though, you can do this:
typedef int (*func_ptr_type)(int);
func_ptr_type myfuncptr;
Unfortunately, there's no corresponding magic trick for arrays.
i would make a array outside of function and just assign it it to your local. this will very likely invoke memcpy or just inline memory copying loop
this is the fastest you can get