Reputation: 703
I've been struggling to wrap my head around this for some reason. I have 15 bits that represent a number. The bits must match a pattern. The pattern is defined in the way the bits start out: they are in the most flush-right representation of that pattern. So say the pattern is 1 4 1. The bits will be:
000000010111101
So the general rule is, take each number in the pattern, create that many bits (1, 4 or 1 in this case) and then have at least one space separating them. So if it's 1 2 6 1 (it will be random):
001011011111101
Starting with the flush-right version, I want to generate every single possible number that meets that pattern. The # of bits will be stored in a variable. So for a simple case, assume it's 5 bits and the initial bit pattern is: 00101. I want to generate:
00101 01001 01010 10001 10010 10100
I'm trying to do this in Objective-C, but anything resembling C would be fine. I just can't seem to come up with a good recursive algorithm for this. It makes sense in the above example, but when I start getting into 12431 and having to keep track of everything it breaks down.
Upvotes: 7
Views: 1056
Reputation: 143194
If your pattern is 00101
, as in the example, then one way you can consider for generating the six patterns is:
Look at the pattern 0000
then choose two of the zeroes to change to ones. Now you'll have something like 0011
. Now just insert a 0
after each 1
(except for the last one). Now you'll have 00101
.
Note that you are choosing two out of four places, and there are six different possible ways you can do that (consistent with what you wrote). Now you just need a way to choose the two bits to flip. You can use something like the algorithm specified at this link: Generating Combinations
Upvotes: 0
Reputation: 414555
Building on @Mark Byers's and Moron's answers your task can be reformulated as follows:
Enumerate all ways to put K
zeros into N
places (see combinations with repetition and Stars and bars).
Example: For 15 bits and 1 2 6 1 pattern there are N=5 places (before/after the number and between 1
s) to put K=2 zeros (number of leading zeros for a flush-right number). Number of ways is binomial(N + K - 1, K) i.e., binomial(5+2-1, 2) = 15.
The key functions in the code below are next_combination_counts()
and comb2number()
.
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#define SIZE(arr) (sizeof(arr)/sizeof(*(arr)))
#define PRInumber "u"
typedef unsigned number_t;
// swap values pointed to by the pointer
static void
iter_swap(int* ia, int* ib) {
int t = *ia;
*ia = *ib;
*ib = t;
}
// see boost::next_combinations_counts()
// http://photon.poly.edu/~hbr/boost/combinations.html
// http://photon.poly.edu/~hbr/boost/combination.hpp
static bool
next_combination_counts(int* first, int* last) {
/*
0 0 2
0 1 1
0 2 0
1 0 1
1 1 0
2 0 0
*/
int* current = last;
while (current != first && *(--current) == 0) {
}
if (current == first) {
if (first != last && *first != 0)
iter_swap(--last, first);
return false;
}
--(*current);
iter_swap(--last, current);
++(*(--current));
return true;
}
// convert combination and pattern to corresponding number
// example: comb=[2, 0, 0] pattern=[1,1] => num=5 (101 binary)
static number_t
comb2number(int comb[], int comb_size, int pattern[], int pattern_size) {
if (pattern_size == 0)
return 0;
assert(pattern_size > 0);
assert(comb_size > pattern_size);
// 111 -> 1000 - 1 -> 2**3 - 1 -> (1 << 3) - 1
// 111 << 2 -> 11100
number_t num = ((1 << pattern[pattern_size-1]) - 1) << comb[pattern_size];
int len = pattern[pattern_size-1] + comb[pattern_size];
for (int i = pattern_size - 1; i--> 0; ) {
num += ((1 << pattern[i]) - 1) << (comb[i+1] + 1 + len);
len += pattern[i] + comb[i+1] + 1;
}
return num;
}
// print binary representation of number
static void
print_binary(number_t number) {
if (number > 0) {
print_binary(number >> 1);
printf("%d", number & 1);
}
}
// print array
static void
printa(int arr[], int size, const char* suffix) {
printf("%s", "{");
for (int i = 0; i < (size - 1); ++i)
printf("%d, ", arr[i]);
if (size > 0)
printf("%d", arr[size - 1]);
printf("}%s", suffix);
}
static void
fill0(int* first, int* last) {
for ( ; first != last; ++first)
*first = 0;
}
// generate {0,0,...,0,nzeros} combination
static void
init_comb(int comb[], int comb_size, int nzeros) {
fill0(comb, comb + comb_size);
comb[comb_size-1] = nzeros;
}
static int
sum(int* first, int* last) {
int s = 0;
for ( ; first != last; ++first)
s += *first;
return s;
}
// calculated max width required to print number (in PRInumber format)
static int
maxwidth(int comb[], int comb_size, int pattern[], int pattern_size) {
int initial_comb[comb_size];
int nzeros = sum(comb, comb + comb_size);
init_comb(initial_comb, comb_size, nzeros);
return snprintf(NULL, 0, "%" PRInumber,
comb2number(initial_comb, comb_size, pattern, pattern_size));
}
static void
process(int comb[], int comb_size, int pattern[], int pattern_size) {
// print combination and pattern
printa(comb, comb_size, " ");
printa(pattern, pattern_size, " ");
// print corresponding number
for (int i = 0; i < comb[0]; ++i)
printf("%s", "0");
number_t number = comb2number(comb, comb_size, pattern, pattern_size);
print_binary(number);
const int width = maxwidth(comb, comb_size, pattern, pattern_size);
printf(" %*" PRInumber "\n", width, number);
}
// reverse the array
static void
reverse(int a[], int n) {
for (int i = 0, j = n - 1; i < j; ++i, --j)
iter_swap(a + i, a + j);
}
// convert number to pattern
// 101101111110100 -> 1, 2, 6, 1
static int
number2pattern(number_t num, int pattern[], int nbits, int comb[]) {
// SIZE(pattern) >= nbits
// SIZE(comb) >= nbits + 1
fill0(pattern, pattern + nbits);
fill0(comb, comb + nbits + 1);
int i = 0;
int pos = 0;
for (; i < nbits && num; ++i) {
// skip trailing zeros
for ( ; num && !(num & 1); num >>= 1, ++pos)
++comb[i];
// count number of 1s
for ( ; num & 1; num >>=1, ++pos)
++pattern[i];
}
assert(i == nbits || pattern[i] == 0);
const int pattern_size = i;
// skip comb[0]
for (int j = 1; j < pattern_size; ++j) --comb[j];
comb[pattern_size] = nbits - pos;
reverse(pattern, pattern_size);
reverse(comb, pattern_size+1);
return pattern_size;
}
int
main(void) {
number_t num = 11769;
const int nbits = 15;
// clear hi bits (required for `comb2number() != num` relation)
if (nbits < 8*sizeof(number_t))
num &= ((number_t)1 << nbits) - 1;
else
assert(nbits == 8*sizeof(number_t));
// `pattern` defines how 1s are distributed in the number
int pattern[nbits];
// `comb` defines how zeros are distributed
int comb[nbits+1];
const int pattern_size = number2pattern(num, pattern, nbits, comb);
const int comb_size = pattern_size + 1;
// check consistency
// . find number of leading zeros in a flush-right version
int nzeros = nbits;
for (int i = 0; i < (pattern_size - 1); ++i)
nzeros -= pattern[i] + 1;
assert(pattern_size > 0);
nzeros -= pattern[pattern_size - 1];
assert(nzeros>=0);
// . the same but using combination
int nzeros_comb = sum(comb, comb + comb_size);
assert(nzeros_comb == nzeros);
// enumerate all combinations
printf("Combination Pattern Binary Decimal\n");
assert(comb2number(comb, comb_size, pattern, pattern_size) == num);
process(comb, comb_size, pattern, pattern_size); // process `num`
// . until flush-left number
while(next_combination_counts(comb, comb + comb_size))
process(comb, comb_size, pattern, pattern_size);
// . until `num` number is encounterd
while (comb2number(comb, comb_size, pattern, pattern_size) != num) {
process(comb, comb_size, pattern, pattern_size);
(void)next_combination_counts(comb, comb + comb_size);
}
return 0;
}
Output:
Combination Pattern Binary Decimal
{1, 0, 0, 1, 0} {1, 2, 6, 1} 010110111111001 11769
{1, 0, 1, 0, 0} {1, 2, 6, 1} 010110011111101 11517
{1, 1, 0, 0, 0} {1, 2, 6, 1} 010011011111101 9981
{2, 0, 0, 0, 0} {1, 2, 6, 1} 001011011111101 5885
{0, 0, 0, 0, 2} {1, 2, 6, 1} 101101111110100 23540
{0, 0, 0, 1, 1} {1, 2, 6, 1} 101101111110010 23538
{0, 0, 0, 2, 0} {1, 2, 6, 1} 101101111110001 23537
{0, 0, 1, 0, 1} {1, 2, 6, 1} 101100111111010 23034
{0, 0, 1, 1, 0} {1, 2, 6, 1} 101100111111001 23033
{0, 0, 2, 0, 0} {1, 2, 6, 1} 101100011111101 22781
{0, 1, 0, 0, 1} {1, 2, 6, 1} 100110111111010 19962
{0, 1, 0, 1, 0} {1, 2, 6, 1} 100110111111001 19961
{0, 1, 1, 0, 0} {1, 2, 6, 1} 100110011111101 19709
{0, 2, 0, 0, 0} {1, 2, 6, 1} 100011011111101 18173
{1, 0, 0, 0, 1} {1, 2, 6, 1} 010110111111010 11770
Upvotes: 1
Reputation: 10275
Is this a theoretical or practical problem? Do you need the optimal O(N) or reasonably good execution time? If this is a practical problem, and unless it's in the inner loop of something, just checking every 15-bit number should be fast enough. It's only 32k numbers.
Just get the partitions for your number, something like this:
void get_groups(ushort x, int* groups) {
int change_group = 0;
while(x) {
if (x & 1) {
++(*groups);
change_group = 1;
} else {
if (change_group) {
++groups;
change_group = 0;
}
}
x >>= 1;
}
}
And then, for every 15-bit number, check if it produces the same groups array as your initial number.
Note: The groups array should be able to accommodate the maximum number of groups (e.g. should have size 8 for 15-bit numbers).
Upvotes: 0
Reputation: 43390
Suppose we have:
000101101
First, count the 0s (5
), and count the groups of 0s surrounded by 1s (1
). We can forget about the 1s for now. The number of zeros per group in any combination can be a list described as (where +
means "or more"):
[0+, 1+, 1+, 0+] where the sum is 6
This is just a variation of a similar problem: find all sets of N non-negative integers that sum to K. For example:
[0+, 0+, 0+, 0+] where the sum is 6
Now, to solve this, start with N=1. The solution is obviously just [6]
. With N=2, the solution is:
[0,6] [1,5] [2,4] [3,3] [4,2] [5,1] [6,0]
It's important to notice an underlying theme here: as the left side gets richer, the right side gets poorer. I'll use Haskell to describe the algorithm, as it turns out to be quite elegant for this type of thing:
sumsTo k n
| n == 1 = [[k]]
| n > 1 = do
i <- [0..k]
s <- sumsTo (k-i) (n-1)
return (i : s)
The n == 1
case is pretty easy to understand: it just returns one combination: [k]
. In the n > 1
case, there's actually a nested loop going on here. It's basically saying:
for each number i from 0 to k
for each s in sumsTo (k-i) (n-1)
prepend i to s
Although this algorithm doesn't quite solve your problem, it's good to know in general.
Now, we want the algorithm to operate differently to handle how the middle list items cannot be zero. For those cases, we want to use i <- [1..k]
instead of i <- [0..k]
. It doesn't matter for the end number since it has no free will (it depends solely on the sum of the previous items). Getting closer, we might say:
sumsTo k n
| n == 1 = [[k]]
| n > 1 = do
i <- [1..k]
s <- sumsTo (k-i) (n-1)
return (i : s)
However, we want our first item to be able to start at zero. To patch that up, we can say:
sumsTo k n first_start
| n == 1 = [[k]]
| n > 1 = do
i <- [first_start..k]
s <- sumsTo (k-i) (n-1) 1
-- make subsequent sumsTo calls start from 1 instead of 0
return (i : s)
This gives us the sequences we need given K
(the number of 0s) and N
(the number of inner groups of 0s plus 2). All that remains is stringifying the sequences (e.g. turning [1,1,0] into "01"). We could go as far as embedding the stringification directly into our recursive algorithm.
Summing it all up, here is a solution in Haskell:
import Data.List
sumsTo k n first_start
| n == 1 = [[k]]
| n > 1 = do
i <- [first_start..k]
s <- sumsTo (k-i) (n-1) 1
return (i : s)
-- remove any xes found at the beginning or end of list
trim x list
= dropWhile (== x)
$ reverse
$ dropWhile (== x)
$ reverse
$ list
-- interleave [1,3,5] [2,4] = [1,2,3,4,5]
interleave xs ys = concat $ transpose [xs,ys]
solve input = solutions where
-- pull out groups of 1s and put them in a list
groupsOfOnes = filter ((== '1') . head) $ group input
-- count 0s
k = length $ filter (== '0') input
-- trim outer 0s
trimmed = trim '0' input
-- count inner groups of 0s
innerGroups = length $ filter ((== '0') . head) $ group trimmed
-- n is number of outer groups (which is innerGroups + 2)
n = innerGroups + 2
-- compute our solution sequences
-- reverse them so our answer will be in lexicographic order
sequences = reverse $ sumsTo k n 0
-- to transform a sequence into a group of zeros,
-- simply make strings of the indicated number of zeros
groupsOfZeros seq = [replicate n '0' | n <- seq]
-- a solution for a sequence is just the zeros interleaved with the ones
solution seq = concat $ interleave (groupsOfZeros seq) groupsOfOnes
solutions = map solution sequences
main = do
input <- getLine
putStr $ unlines $ solve input
In conclusion, I recommend learning Haskell so you can prototype algorithms more quickly :-)
Upvotes: 0
Reputation:
Hopefully, this will make it easier to wrap your head around it (please read through this with a pen and paper in hand).
Say the number of zeroes (starting from the right) is x1, x2, ..., xn. eg: if the bit pattern is 00001110001001 then x1 = 0, x2 = 2, x3 = 3, x4 = 4. n is one more than the number of blocks of ones. Observe that knowing x1, x2, ..., xn is enough to figure out the bit-pattern.
Now if the total number of 1's you have is S and total number of bits you have available is M then we must have that
x1 + x2 + ... + xn = M - S
and x1 ≥ 0, xn ≥ 0, x2 ≥ 1, x3 ≥ 1, ...
Let z1 = x1 + 1 and zn = xn + 1
Thus we have
z1 + x2 + ... xn-1 + zn = M - S + 2
Where z1 ≥ 1, x2 ≥ 1, x3 ≥ 1, ..., zn ≥ 1.
Now consider a partition of M-S+2 items where each partition has at least one item. Any partition corresponds to a solution of the above equation and a solution corresponds to a partition in a 1-1 fashion.
Place the M-S+2 items along a line. To get a partition, consider placing n-1 sticks in the M-S+2-1 = M-S+1 spots available, between the items.
Thus a solution (and ultimately your required bit-pattern) uniquely corresponds to a way of choosing n-1 spots among M-S+1 spots.
In case of 5 bits, and 1 bits being 1 and 1.
You have n = 3, M = 5, and S = 2.
Thus you have M-S+1 choose n-1 = 4 choose 2 = 6 possiblities.
Enumerating n choose r combinations is a standard problem and you should find a large variety of solutions (some of them very clever!) for that on the web.
For an example see here: http://compprog.files.wordpress.com/2007/10/comb1.c which seems to support a 'lazy' enumeration: next_combination and does not require huge amounts of memory.
Upvotes: 3
Reputation: 838666
I'm not going to give you Objective-C code mainly because:
Instead I will give you some ideas and some code showing how I would implement this in a higher language with generators and garbage collection (Python in this case) and a hint on how to do it without generators. Hopefully someone else may be able to port the code for you if you cannot do it yourself.
I would think about your problem in a slightly different way:
In your last example you have two leading zeros and three partitions with separators '10' and '1':
2 0 0: 00101 1 1 0: 01001 1 0 1: 01010 0 2 0: 10001 0 1 1: 10010 0 0 2: 10100
The separators are always of the form 111..10
except the last which is just 111..1
without the trailing zero.
To enumerate the above partitions use a function like the following in Python:
def partitions(n, x):
if n == 1:
yield [x]
else:
for i in range(x + 1):
for p in partitions(n - 1, x - i):
yield [i] + p
for p in partitions(3, 2):
print p
Result:
[0, 0, 2]
[0, 1, 1]
[0, 2, 0]
[1, 0, 1]
[1, 1, 0]
[2, 0, 0]
Once you have these partitions it is simple to construct the patterns.
One challenge is that Objective-C doesn't have built-in support for the yield construct. The following rewrite of the above function may be easier to convert to Objective-C:
def partitions(n, x):
if n == 1:
return [[x]]
else:
result = []
for i in range(x + 1):
for p in partitions(n - 1, x - i):
result.append([i] + p)
return result
I hope that is of some use to you.
Upvotes: 2