I have a algorithm problem that I need to speed up :)
I need a 32bit random number, with exact 10 bits set to 1. But in the same time, patterns like 101 (5 dec) and 11 (3 dec) to be considered illegal.
Now the MCU is a 8051 (8 bit) and I tested all this in Keil uVision. My first attempt completes, giving the solution
0x48891249
1001000100010010001001001001001 // correct, 10 bits 1, no 101 or 11
The problem is that it completes in 97 Seconds or 1165570706 CPU cycles which is ridiculous!!!
Here is my code
// returns 1 if number is not good. ie. contains at leats one 101 bit sequence
bool checkFive(unsigned long num)
{
unsigned char tmp;
do {
tmp = (unsigned char)num;
if(
(tmp & 7) == 5
|| (tmp & 3) == 3
) // illegal pattern 11 or 101
return true; // found
num >>= 1;
}while(num);
return false;
}
void main(void) {
unsigned long v,num; // count the number of bits set in v
unsigned long c; // c accumulates the total bits set in v
do {
num = (unsigned long)rand() << 16 | rand();
v = num;
// count all 1 bits, Kernigen style
for (c = 0; v; c++)
v &= v - 1; // clear the least significant bit set
}while(c != 10 || checkFive(num));
while(1);
}
The big question for a brilliant mind :) Can be done faster? Seems that my approach is naive.
Thank you in advance,
Wow, I'm impressed, thanks all for suggestions. However, before accept, I need to test them these days.
Now with the first option (look-up) it's just not realistic, will complete blow my 4K RAM of entire 8051 micro controller :) As you can see in image bellow, I tested for all combinations in Code Blocks but there are way more than 300 and it's not finished yet until 5000 index...
The code I use to test
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
//#define bool bit
//#define true 1
//#define false 0
// returns 1 if number is not good. ie. contains at leats one 101 bit sequence
bool checkFive(uint32_t num)
{
uint8_t tmp;
do {
tmp = (unsigned char)num;
if(
(tmp & 7) == 5
|| (tmp & 3) == 3
) // illegal pattern 11 or 101
return true; // found
num >>= 1;
}while(num);
return false;
}
void main(void) {
uint32_t v,num; // count the number of bits set in v
uint32_t c, count=0; // c accumulates the total bits set in v
//printf("Program started \n");
num = 0;
printf("Program started \n");
for(num=0; num <= 0xFFFFFFFF; num++)
{
//do {
//num = (uint32_t)rand() << 16 | rand();
v = num;
// count all 1 bits, Kernigen style
for (c = 0; v; c++)
v &= v - 1; // clear the least significant bit set
//}while(c != 10 || checkFive(num));
if(c != 10 || checkFive(num))
continue;
count++;
printf("%d: %04X\n", count, num);
}
printf("Complete \n");
while(1);
}
Perhaps I can re-formulate the problem:
I need a number with:
So somehow, shuffle only the 1 bits inside.
Or, take a 0x00000000 and add just 10 of 1 bits in random positions, except the illegal patterns.
C library function - rand()The C library function int rand(void) returns a pseudo-random number in the range of 0 to RAND_MAX. RAND_MAX is a constant whose default value may vary between implementations but it is granted to be at least 32767.
rand() The function rand() is used to generate the pseudo random number. It returns an integer value and its range is from 0 to rand_max i.e 32767.
The rand() function in C++ is used to generate random numbers; it will generate the same number every time we run the program. In order to seed the rand() function, srand(unsigned int seed) is used. The srand() function sets the initial point for generating the pseudo-random numbers.
Given a routine r(n)
that returns a random integer from 0 (inclusive) to n
(exclusive) with uniform distribution, the values described in the question may be generated with a uniform distribution by calls to P(10, 4)
where P
is:
static uint32_t P(int a, int b)
{
if (a == 0 && b == 0)
return 0;
else
return r(a+b) < a ? P(a-1, b) << 3 | 1 : P(a, b-1) << 1;
}
The required random number generator can be:
static int r(int a)
{
int q;
do
q = rand() / ((RAND_MAX+1u)/a);
while (a <= q);
return q;
}
(The purpose of dividing by (RAND_MAX+1u)/a
and the do-while
loop is to trim the range of rand
to an even multiple of a
so that bias due to a non-multiple range is eliminated.)
(The recursion in P
may be converted to iteration. This is omitted as it is unnecessary to illustrate the algorithm.)
If the number cannot contain consecutive bits 11
or 101
, then the closest together two 1
bits can be is three bits apart, as in 1001
. Fitting ten 1
bits in 32 bits then requires at least 28 bits, as in 1001001001001001001001001001
. Therefore, to satisfy the constraints that there is no 11
or 101
and there are exactly 10 1
bits, the value must be 1001001001001001001001001001
with four 0
bits inserted in some positions (including possibly the beginning or the end).
Selecting such a value is equivalent to placing 10 instances of 001
and 4 instances of 0
in some order.1 There are 14! ways of ordering 14 items, but any of the 10! ways of rearranging the 10 001
instances with each other are identical, and any of the 4! ways of rearranging the 0
instances with each other are identical, so the number of distinct selections is 14! / 10! / 4!, also known as the number of combinations of selecting 10 things from 14. This is 1,001.
To perform such a selection with uniform distribution, we can use a recursive algorithm:
When ordering a instances of one object and b of a second object, a/(a+b) of the potential orderings will start with the first object, and b/(a+b) will start with the second object. Thus, the design of the P
routine is:
001
and then recurse to select an order for a-1 instances of 001
and b instances of 0
.0
and then recurse to select an order for a instances of 001
and b-1 instances of 0
.(Since, once a
is zero, only 0
instances are generated, if (a == 0 && b == 0)
in P
may be changed to if (a == 0)
. I left it in the former form as that shows the general form of a solution in case other strings are involved.)
Here is a program to list all values (although not in ascending order).
#include <stdint.h>
#include <stdio.h>
static void P(uint32_t x, int a, int b)
{
if (a == 0 && b == 0)
printf("0x%x\n", x);
else
{
if (0 < a) P(x << 3 | 1, a-1, b);
if (0 < b) P(x << 1, a, b-1);
}
}
int main(void)
{
P(0, 10, 4);
}
1 This formulation means we end up with a string starting 001…
rather than 1…
, but the resulting value, interpreted as binary, is equivalent, even if there are instances of 0
inserted ahead of it. So the strings with 10 001
and 4 0
are in one-to-one correspondence with the strings with 4 0
inserted into 1001001001001001001001001001
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With