Embedded C code review - c

I am required to write a function that uses a look up table for ADC values for temperature sensor analog input, and it finds out the temperature given an ADC value by "interpolating" - linear approximation.
I have created a function and written some test cases for it, I want to know if there is something which you guys can suggest to improve the code, since this is supposed to be for an embedded uC, probably stm32.
I am posting my code and attaching my C file, it will compile and run.
Please let me know if you have any comments/suggestions for improvement.
I also want to know a bit about the casting from uint32_t to float that I am doing, if it is efficient way to code.
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define TEMP_ADC_TABLE_SIZE 15
typedef struct
{
int8_t temp;
uint16_t ADC;
}Temp_ADC_t;
const Temp_ADC_t temp_ADC[TEMP_ADC_TABLE_SIZE] =
{
{-40,880}, {-30,750},
{-20,680}, {-10,595},
{0,500}, {10,450},
{20,410}, {30,396},
{40,390}, {50,386},
{60,375}, {70,360},
{80,340}, {90,325},
{100,310}
};
// This function finds the indices between which the input reading lies.
// It uses an algorithm that doesn't need to loop through all the values in the
// table but instead it keeps dividing the table in two half until it finds
// the indices between which the value is or the exact index.
//
// index_low, index_high, are set to the indices if a value is between sample
// points, otherwise if there is an exact match then index_mid is set.
//
// Returns 0 on error, 1 if indices found, 2 if exact index is found.
uint8_t find_indices(uint16_t ADC_reading,
const Temp_ADC_t table[],
int8_t dir,
uint16_t* index_low,
uint16_t* index_high,
uint16_t* index_mid,
uint16_t table_size)
{
uint8_t found = 0;
uint16_t mid, low, high;
low = 0;
high = table_size - 1;
if((table != NULL) && (table_size > 0) && (index_low != NULL) &&
(index_mid != NULL) && (index_high != NULL))
{
while(found == 0)
{
mid = (low + high) / 2;
if(table[mid].ADC == ADC_reading)
{
// exact match
found = 2;
}
else if(table[mid].ADC < ADC_reading)
{
if(table[mid + dir].ADC == ADC_reading)
{
// exact match
found = 2;
mid = mid + dir;
}
else if(table[mid + dir].ADC > ADC_reading)
{
// found the two indices
found = 1;
low = (dir == 1)? mid : (mid + dir);
high = (dir == 1)? (mid + dir) : mid;
}
else if(table[mid + dir].ADC < ADC_reading)
{
low = (dir == 1)? (mid + dir) : low;
high = (dir == 1) ? high : (mid + dir);
}
}
else if(table[mid].ADC > ADC_reading)
{
if(table[mid - dir].ADC == ADC_reading)
{
// exact match
found = 2;
mid = mid - dir;
}
else if(table[mid - dir].ADC < ADC_reading)
{
// found the two indices
found = 1;
low = (dir == 1)? (mid - dir) : mid;
high = (dir == 1)? mid : (mid - dir);
}
else if(table[mid - dir].ADC > ADC_reading)
{
low = (dir == 1)? low : (mid - dir);
high = (dir == 1) ? (mid - dir) : high;
}
}
}
*index_low = low;
*index_high = high;
*index_mid = mid;
}
return found;
}
// This function uses the lookup table provided as an input argument to find the
// temperature for a ADC value using linear approximation.
//
// Temperature value is set using the temp pointer.
//
// Return 0 if an error occured, 1 if an approximate result is calculate, 2
// if the sample value match is found.
uint8_t lookup_temp(uint16_t ADC_reading, const Temp_ADC_t table[],
uint16_t table_size ,int8_t* temp)
{
uint16_t mid, low, high;
int8_t dir;
uint8_t return_code = 1;
float gradient, offset;
low = 0;
high = table_size - 1;
if((table != NULL) && (temp != NULL) && (table_size > 0))
{
// Check if ADC_reading is out of bound and find if values are
// increasing or decreasing along the table.
if(table[low].ADC < table[high].ADC)
{
if(table[low].ADC > ADC_reading)
{
return_code = 0;
}
else if(table[high].ADC < ADC_reading)
{
return_code = 0;
}
dir = 1;
}
else
{
if(table[low].ADC < ADC_reading)
{
return_code = 0;
}
else if(table[high].ADC > ADC_reading)
{
return_code = 0;
}
dir = -1;
}
}
else
{
return_code = 0;
}
// determine the temperature by interpolating
if(return_code > 0)
{
return_code = find_indices(ADC_reading, table, dir, &low, &high, &mid,
table_size);
if(return_code == 2)
{
*temp = table[mid].temp;
}
else if(return_code == 1)
{
gradient = ((float)(table[high].temp - table[low].temp)) /
((float)(table[high].ADC - table[low].ADC));
offset = (float)table[low].temp - gradient * table[low].ADC;
*temp = (int8_t)(gradient * ADC_reading + offset);
}
}
return return_code;
}
int main(int argc, char *argv[])
{
int8_t temp = 0;
uint8_t x = 0;
uint16_t u = 0;
uint8_t return_code = 0;
uint8_t i;
//Print Table
printf("Lookup Table:\n");
for(i = 0; i < TEMP_ADC_TABLE_SIZE; i++)
{
printf("%d,%d\n", temp_ADC[i].temp, temp_ADC[i].ADC);
}
// Test case 1
printf("Test case 1: Find the temperature for ADC Reading of 317\n");
printf("Temperature should be 95 Return Code should be 1\n");
return_code = lookup_temp(317, temp_ADC, TEMP_ADC_TABLE_SIZE, &temp);
printf("Temperature: %d C\n", temp);
printf("Return code: %d\n\n", return_code);
// Test case 2
printf("Test case 2: Find the temperature for ADC Reading of 595 (sample value)\n");
printf("Temperature should be -10, Return Code should be 2\n");
return_code = lookup_temp(595, temp_ADC, TEMP_ADC_TABLE_SIZE, &temp);
printf("Temperature: %d C\n", temp);
printf("Return code: %d\n\n", return_code);
// Test case 3
printf("Test case 3: Find the temperature for ADC Reading of 900 (out of bound - lower)\n");
printf("Return Code should be 0\n");
return_code = lookup_temp(900, temp_ADC, TEMP_ADC_TABLE_SIZE, &temp);
printf("Return code: %d\n\n", return_code);
// Test case 4
printf("Test case 4: Find the temperature for ADC Reading of 300 (out of bound - Upper)\n");
printf("Return Code should be 0\n");
return_code = lookup_temp(300, temp_ADC, TEMP_ADC_TABLE_SIZE, &temp);
printf("Return code: %d\n\n", return_code);
// Test case 5
printf("Test case 5: NULL pointer (Table pointer) handling\n");
printf("Return Code should be 0\n");
return_code = lookup_temp(595, NULL, TEMP_ADC_TABLE_SIZE, &temp);
printf("Return code: %d\n\n", return_code);
// Test case 6
printf("Test case 6: NULL pointer (temperature result pointer) handling\n");
printf("Return Code should be 0\n");
return_code = lookup_temp(595, temp_ADC, TEMP_ADC_TABLE_SIZE, NULL);
printf("Return code: %d\n", return_code);
// Test case 7
printf("Test case 7: Find the temperature for ADC Reading of 620\n");
printf("Temperature should be -14 Return Code should be 1\n");
return_code = lookup_temp(630, temp_ADC, TEMP_ADC_TABLE_SIZE, &temp);
printf("Temperature: %d C\n", temp);
printf("Return code: %d\n\n", return_code);
// Test case 8
printf("Test case 8: Find the temperature for ADC Reading of 880 (First table element test)\n");
printf("Temperature should be -40 Return Code should be 2\n");
return_code = lookup_temp(880, temp_ADC, TEMP_ADC_TABLE_SIZE, &temp);
printf("Temperature: %d C\n", temp);
printf("Return code: %d\n\n", return_code);
// Test case 9
printf("Test case 9: Find the temperature for ADC Reading of 310 (Last table element test)\n");
printf("Temperature should be 100 Return Code should be 2\n");
return_code = lookup_temp(310, temp_ADC, TEMP_ADC_TABLE_SIZE, &temp);
printf("Temperature: %d C\n", temp);
printf("Return code: %d\n\n", return_code);
printf("Press ENTER to continue...\n");
getchar();
return 0;
}

I generally compute the lookup table offline and the runtime code boils down to:
temp = table[dac_value];
Particularly if going embedded, you dont want floating point, often dont need it. Pre-computing the table solves that problem as well.
Pre-computing also solves the problem of having an efficient algorithm, you can be as sloppy and slow as you want, you only have to do this computation rarely. No algorithm is going to be able to compete with the lookup table at runtime. So long as you have room for the look up table it is a win-win. If you dont have say 256 locations in prom for an 8 bit dac for example you might have 128 locations, and you can do a little real-time interpolation:
//TODO add special case for max dac_value and max dac_value-1 or make the table 129 entries deep
if(dac_value&1)
{
temp=(table[(dac_value>>1)+0]+table[(dac_value>>1)+1])>>1;
}
else
{
temp=table[dac_value>>1];
}
I often find that the table being fed in can and will change. Yours may be cast in stone, but this same kind of computation comes about with calibrated devices. And you have done the right thing by checking that the data is in the right general direction (decreasing relative to the dac increasing or increasing relative to dac values increasing) and more importantly check for divide by zero. Despite being a hard coded table develop habits with the expectation that it will change to a different hard coded table with the desire to not have to change your interpolation code every time.
I also believe that the raw dac value is the most important value here, the computed temperature can happen at any time. Even if the conversion to degrees of some flavor has been cast in stone, it is a good idea to display or store the raw dac value along with the computed temperature. You can always re-compute the temperature from the dac value, but you cannot always accurately reproduce the raw dac value from the computed value. It depends on what you are building naturally, if this is a thermostat for public use in their homes they dont want to have some hex value on the display. But if this is any kind of test or engineering environment where you are collecting data for later analysis or verification that some product is good or bad, carrying around that dac value can be a good thing. It only takes once or twice for a situation where the engineer that provided you with the table, claims it was the final table then changes it. Now you have to go back to all the logs that used the incorrect table, compute back to the dac value using the prior table and re-compute the temp using the new table and write a new log file. If you had the raw dac value there and everyone was trained to think in terms of dac values and that the temperature was simply a reference, you might not have to repair older log values for each new calibration table. The worst case is having only the temperature in the log file and not being able to determine which cal table was used for that log file, the log file becomes invalid the unit tested becomes a risk item, etc.

Why does your bisection search have to handle both ascending and descending tables? The table is hard coded anyhow, so prepare your table offline, and you halve the complexity. You also generally don't need to do half your comparisons - just stop when high and low are adjacent or equal.
In such as small program, there is very little point checking for non-null table and other inputs and silently reporting no match - either return and error code, or make sure that the one place the function is called it's not being called with invalid pointers ( there is no reason to assume that an invalid pointer is NULL, just because NULL may be an invalid pointer on some systems).
Without the extra complexities, the search would become something like:
enum Match { MATCH_ERROR, MATCH_EXACT, MATCH_INTERPOLATE, MATCH_UNDERFLOW, MATCH_OVERFLOW };
enum Match find_indices (uint16_t ADC_reading,
const Temp_ADC_t table[],
uint16_t* index_low,
uint16_t* index_high )
{
uint16_t low = *index_low;
uint16_t high = *index_high;
if ( low >= high ) return MATCH_ERROR;
if ( ADC_reading < table [ low ].ADC ) return MATCH_UNDERFLOW;
if ( ADC_reading > table [ high ].ADC ) return MATCH_OVERFLOW;
while ( low < high - 1 )
{
uint16_t mid = ( low + high ) / 2;
uint16_t val = table [ mid ].ADC;
if ( ADC_reading > val)
{
low = mid;
continue;
}
if ( ADC_reading < val )
{
high = mid;
continue;
}
low = high = mid;
break;
}
*index_low = low;
*index_high = high;
if ( low == high )
return MATCH_EXACT;
else
return MATCH_INTERPOLATE;
}
Since the table has been pre-prepared to be ascending, and search returns a meaningful enum rather than an integer code, you don't need that much in lookup_temp:
enum Match lookup_temp ( uint16_t ADC_reading, const Temp_ADC_t table[],
uint16_t table_size, int8_t* temp)
{
uint16_t low = 0;
uint16_t high = table_size - 1;
enum Match match = find_indices ( ADC_reading, table, &low, &high );
switch ( match ) {
case MATCH_INTERPOLATE:
{
float gradient = ((float)(table[high].temp - table[low].temp)) /
((float)(table[high].ADC - table[low].ADC));
float offset = (float)table[low].temp - gradient * table[low].ADC;
*temp = (int8_t)(gradient * ADC_reading + offset);
break;
}
case MATCH_EXACT:
*temp = table[low].temp;
break;
}
return match;
}
Given all terms in the gradient calculation are 16 bit ints, you could perform the interpolation in 32 bits, as long as you calculate all terms of the numerator before dividing:
*temp = temp_low + uint16_t ( ( uint32_t ( ADC_reading - adc_low ) * uint32_t ( temp_high - temp_low ) ) / uint32_t ( adc_high - adc_low ) );

There is a lot you can improve.
First of all the best integer data type depends on the machine (word size). I don't know how are your int8_t and uint16_t declared.
Also, not for performance but for readability, I usually don't use "cascading" ifs, like
if condition
{
if another_condition
{
if third condition
{
but instead:
if not condition
return false;
// Here the condition IS true, thus no reason to indent
Another point of attention:
low = (dir == 1)? mid : (mid + dir);
high = (dir == 1)? (mid + dir) : mid;
you do the dir==1 twice, better to use ifs:
int sum = mid+dir;
if dir == 1
{
low = mid;
high = sum;
}
else
{
low=sum;
high=mid;
}
But there's more to say. For example you could use a faster searching algorithm.

It is good that you include the test framework, but your test framework lacks rigour and abuses the DRY (Don't Repeat Yourself) principle.
static const struct test_case
{
int inval; /* Test reading */
int rcode; /* Expected return code */
int rtemp; /* Expected temperature */
} test[] =
{
{ 317, 1, 95 },
{ 595, 1, -10 },
{ 900, 0, 0 }, // Out of bound - lower
{ 300, 0, 0 }, // Out of bound - upper
{ 620, 1, -14 },
{ 880, 2, -40 }, // First table element
{ 310, 2, 100 }, // Last table element
};
Now you can write the test code for a single test in a function:
static int test_one(int testnum, const struct test_case *test)
{
int result = 0;
int temp;
int code = lookup_temp(test->inval, temp_ADC, TEMP_ADC_TABLE_SIZE, &temp);
if (temp == test->rtemp && code == test->rcode)
printf("PASS %d: reading %d, code %d, temperature %d\n",
testnum, test->inval, code, temp);
else
{
printf("FAIL %d: reading %d, code (got %d, wanted %d), "
"temperature (got %d, wanted %d)\n",
testnum, test->inval, code, test->rcode, temp, test->rtemp);
result = 1;
}
}
And then the main program can have a loop that drives the test function:
#define DIM(x) (sizeof(x)/sizeof(*(x)))
int failures = 0;
int i;
for (i = 0; i < DIM(test); i++)
failures += test_one(i + 1, &test[i]);
if (failures != 0)
printf("!! FAIL !! (%d of %d tests failed)\n", failures, (int)DIM(test));
else
printf("== PASS == (%d tests passed)\n", (int)DIM(test));
Now if there is a problem with any of the tests, it is going to be hard to excuse not spotting the problem. With your original code, someone could overlook the mistake.
Clearly, if you want the comments about the tests too, you can add a const char *tag to the array and supply and print those tags. If you really want to get fancy, you can even encode the null pointer tests (kudos for including those) in the regime by including appropriately initialized pointers in the array - you might add a pair of bit-flags for 'table is null' and 'temperature pointer is null' and conditional code in the function.

Related

Minimax algorithm behaving strangely

I am working on assignment. I want to create a game that is simulated and played by the computer. It is a logical game and the goal is to collect the most tokens with the highest value. Program starts by asking for input in this format('END' signals its the end of input, working on removing it):
N:1,2
W:3,5
E:9,1,1,1
S:1,7
END
Letters stand for direction like East, West, North, South. Numbers after the directions are tokens with value. You can only pick tokens from end of an direction. Player that collects most valuable tokens wins.
I need to make the game to play itself the most optimal way, found minimax algorithm. But I am completly clueless how to implement it correctly.
I am asking some kind soul, to help me make it work correctly. Give some tips at least :)
This is what I tried. Its working somehow but not the most optimal way. For example in the input I provided the most optimal result is:
A/B: 15/16
But I am getting:
A/B: 14/17
My code here.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#define MAX_TOKENS 100
struct Game
{
//lists with the values ​​of the tokens on the individual arms of the cross
int *north;
int *west;
int *east;
int *south;
//lengths of token value lists
int north_size;
int west_size;
int east_size;
int south_size;
//scores of players A and B
int score_a;
int score_b;
};
void remove_token(struct Game *game, int player, char direction)
{
//variable for the token taken
int token;
//according to the direction, remove the token and add its value to the player's score
switch (direction)
{
case 'N':
//remove the token from the N arm
token = game->north[game->north_size - 1];
//reduce the length of the token value list by 1
game->north_size--;
//add the chip value to the player's score
if (player == 0)
game->score_a += token;
else
game->score_b += token;
break;
case 'W':
//remove the token from the W arm
token = game->west[game->west_size - 1];
//reduce the length of the token value list by 1
game->west_size--;
//add the chip value to the player's score
if (player == 0)
game->score_a += token;
else
game->score_b += token;
break;
case 'E':
//remove the token from the E arm
token = game->east[game->east_size - 1];
//reduce the length of the token value list by 1
game->east_size--;
//add the chip value to the player's score
if (player == 0)
game->score_a += token;
else
game->score_b += token;
break;
case 'S':
//remove the token from the S arm
token = game->south[game->south_size - 1];
//reduce the length of the token value list by 1
game->south_size--;
//add the chip value to the player's score
if (player == 0)
game->score_a += token;
else
game->score_b += token;
break;
default:
//throw an error if the player entered an invalid direction
printf("Neplatný směr pro odebrání žetonu!\n");
}
}
char minimax(struct Game *game, int depth, int player, int alpha, int beta)
{
//if all arms are empty or we have reached the maximum depth, return the best direction
if (game->north_size == 0 && game->west_size == 0 && game->east_size == 0 && game->south_size == 0 || depth == 0)
return 'X';
//the best move value for player A
int best_score_a = INT_MIN;
//the best move value for player B
int best_score_b = INT_MAX;
//direction for best move
char best_direction;
//go through all arms to find the best move
if (game->north_size > 0)
{
//copy the game state
struct Game game_copy = *game;
//remove the token from the N arm
remove_token(&game_copy, player, 'N');
//find out the best move for your opponent
int score = minimax(&game_copy, depth - 1, player == 0 ? 1 : 0, alpha, beta);
//update the best move for player A
if (player == 0 && score > best_score_a)
{
best_score_a = score;
best_direction = 'N';
}
//update the best move for player B
if (player == 1 && score < best_score_b)
{
best_score_b = score;
best_direction = 'N';
}
//update alpha and beta
if (player == 0)
alpha = fmax(alpha, score);
else
beta = fmin(beta, score);
//if beta is less than alpha, we end traversing the tree
if (beta <= alpha)
return best_direction;
}
if (game->west_size > 0)
{
//copy the game state
struct Game game_copy = *game;
//remove the token from the W arm
remove_token(&game_copy, player, 'W');
//find out the best move for your opponent
int score = minimax(&game_copy, depth - 1, player == 0 ? 1 : 0, alpha, beta);
//update the best move for player A
if (player == 0 && score > best_score_a)
{
best_score_a = score;
best_direction = 'W';
}
//update the best move for player B
if (player == 1 && score < best_score_b)
{
best_score_b = score;
best_direction = 'W';
}
//update alpha and beta
if (player == 0)
alpha = fmax(alpha, score);
else
beta = fmin(beta, score);
//if beta is less than alpha, we end traversing the tree
if (beta <= alpha)
return best_direction;
}
if (game->east_size > 0)
{
//copy the game state
struct Game game_copy = *game;
//remove the token from the E arm
remove_token(&game_copy, player, 'E');
//find out the best move for your opponent
int score = minimax(&game_copy, depth - 1, player == 0 ? 1 : 0, alpha, beta);
//update the best move for player A
if (player == 0 && score > best_score_a)
{
best_score_a = score;
best_direction = 'E';
}
//update the best move for player B
if (player == 1 && score < best_score_b)
{
best_score_b = score;
best_direction = 'E';
}
//update alpha and beta
if (player == 0)
alpha = fmax(alpha, score);
else
beta = fmin(beta, score);
//if beta is less than alpha, we end traversing the tree
if (beta <= alpha)
return best_direction;
}
if (game->south_size > 0)
{
//copy the game state
struct Game game_copy = *game;
//remove the token from the S arm
remove_token(&game_copy, player, 'S');
//find out the best move for your opponent
int score = minimax(&game_copy, depth - 1, player == 0 ? 1 : 0, alpha, beta);
//update the best move for player A
if (player == 0 && score > best_score_a)
{
best_score_a = score;
best_direction = 'S';
}
//update as soon as possible
if (player == 1 && score < best_score_b)
{
best_score_b = score;
best_direction = 'S';
}
//update alpha and beta
if (player == 0)
alpha = fmax(alpha, score);
else
beta = fmin(beta, score);
//if beta is less than alpha, we end traversing the tree
if (beta <= alpha)
return best_direction;
}
//return the best move
return player == 0 ? best_direction : best_direction;
}
void read_tokens(int *north, int *west, int *east, int *south, int *north_size, int *west_size, int *east_size, int *south_size)
{
//buffer for reading in input
char buffer[MAX_TOKENS];
//read in the input line by line
while (fgets(buffer, MAX_TOKENS, stdin) != NULL)
{
//remove the newline character from the end of the line
buffer[strcspn(buffer, "\n")] = 0;
//check for the "END" string to end the input
if (strcmp(buffer, "END") == 0)
break;
//split the line at the colon character
char *direction = strtok(buffer, ":");
char *tokens = strtok(NULL, ":");
//split the tokens at each comma
char *token = strtok(tokens, ",");
//determine the direction and store the tokens in the appropriate array
if (strcmp(direction, "N") == 0)
{
while (token != NULL)
{
north[*north_size] = atoi(token);
(*north_size)++;
token = strtok(NULL, ",");
}
}
else if (strcmp(direction, "W") == 0)
{
while (token != NULL)
{
west[*west_size] = atoi(token);
(*west_size)++;
token = strtok(NULL, ",");
}
}
else if (strcmp(direction, "E") == 0)
{
while (token != NULL)
{
east[*east_size] = atoi(token);
(*east_size)++;
token = strtok(NULL, ",");
}
}
else if (strcmp(direction, "S") == 0)
{
while (token != NULL)
{
south[*south_size] = atoi(token);
(*south_size)++;
token = strtok(NULL, ",");
}
}
else
{
//invalid direction = error
printf("Nespravny vstup.\n");
}
}
}
void print_progress(struct Game *game, int player, char direction)
{
char letter_player = ' ';
if (player == 0)
{
letter_player = 'A';
}
else
letter_player = 'B';
//printing of individual steps
switch (direction)
{
case 'N':
printf("%c: %c[%d] (%d)\n", letter_player, direction, game->north_size, game->north[game->north_size - 1]);
break;
case 'W':
printf("%c: %c[%d] (%d)\n", letter_player, direction, game->west_size, game->west[game->west_size - 1]);
break;
case 'E':
printf("%c: %c[%d] (%d)\n", letter_player, direction, game->east_size, game->east[game->east_size - 1]);
break;
case 'S':
printf("%c: %c[%d] (%d)\n", letter_player, direction, game->south_size, game->south[game->south_size - 1]);
break;
default:
break;
}
}
void play(struct Game *game, int depth)
{
//variable for current player (A or B)
int player = 0;
//until all chips are taken, alternate players taking chips
while (game->north_size > 0 || game->west_size > 0 || game->east_size > 0 || game->south_size > 0)
{
//player A
if (player == 0)
{
//function on the selection of a token
char direction = minimax(game, depth, 0, INT_MIN, INT_MAX);
print_progress(game, player, direction);
//remove the token from the game
remove_token(game, player, direction);
}
//player B
else
{
//function on the selection of a token
char direction = minimax(game, depth, 1, INT_MIN, INT_MAX);
print_progress(game, player, direction);
//remove the token from the game
remove_token(game, player, direction);
}
//switch players
player = (player + 1) % 2;
}
}
int main(void)
{
//field for token values
int north[MAX_TOKENS], west[MAX_TOKENS], east[MAX_TOKENS], south[MAX_TOKENS];
//sizes of token value fields
int north_size = 0, west_size = 0, east_size = 0, south_size = 0;
printf("Input:\n");
//fetch token values ​​from input
read_tokens(north, west, east, south, &north_size, &west_size, &east_size, &south_size);
//creating a game
struct Game game;
game.north = north;
game.west = west;
game.east = east;
game.south = south;
game.north_size = north_size;
game.west_size = west_size;
game.east_size = east_size;
game.south_size = south_size;
game.score_a = 0;
game.score_b = 0;
//set the maximum depth of the minimax search tree
int depth = 1;
//start the game using the play() function
play(&game, depth);
//evaluation of the result of the game
printf("Celkem A/B: %d/%d\n", game.score_a, game.score_b);
return 0;
}
This is not an answer. The OP did ask for 'tips', though. Here are a few:
Don't drag-in the floating point library (math.h) for a single function ( fmax() ) that you could/should implement, especially as you are dealing with integer values and results.
Here are two 'branchless' statements returning the min or max of two integers: i & j.
int minVal = j ^ ((i^j) & -(i<j));
int maxVal = j ^ ((i^j) & -(i>j));
You can safely drop END from your data file. fgets() will find 4 lines and then return NULL, finishing the loading of the initial values.
As it stands, the initial values are single ASCII digits. Rather than calling atoi(), merely subtracting the ASCII character '0' from each ASCII digit will yield the (binary) integer value intended. (You might consider using 'A'-'Z' in the initial data, instead of '0' - '9', to both increase the range of points and obscure the best path to winning the game.
3a) Instead of "N:1,5,6,4", the data file could contain "N1564" obviating the need for using strtok()... Simply use each character in sequence.
It's quite understandable that you, as a beginner, have coded to the "literal" game involving 4 points of a compass. You've written code that would be very difficult to change to having 5 or 6 (or more) 'stack's of values to choose from.
If, instead of discrete variables named 'north...', 'west...', etc., had you used an array of 4 elements, most of the copy/paste/adapt that has bloated the source code's size would melt down to processing each of the 4 (or 5 or 6) 'stacks' in a loop. imo, the art of coding is balancing the drive to "make something work" with the necessity of code re-use wherever possible. Look for redundancy and strive to eliminate that.
Your code eliminates the trailing '\n' after fgets() has filled the input buffer. You then go on to translate an array of characters into arrays of discrete integer values. Then, the minimax function recusively trims copies of those arrays as it plays the game.
Consider keeping and working with the original array of characters (the string) instead. When "the user" picks the last 'character' (top of the stack) as their 'move', simply shorten the string of that stack. (Use C's string.h string handling capabilities instead of doing the work yourself with your own arrays of ints.) (The string "" has no more available tokens. Your code doesn't need to maintain a counter for each 'direction'.)
(This might be easiest.) When you've moved to using 4 arrays - one for each of 'N', 'W', 'E' and 'S' - you will see that the fixation of compass points has blinded you to using 'A', 'B', 'C' and 'D' instead... Given the "non-special" nature of "ABCD", it becomes a goal to write code that would work for "ABCDE" or "ABCDEF".
Summary: By replacing replicated code with re-used code, your program will shrink down to something easily manageable. When functionality is re-used and made 'general purpose', you will likely be able to improve the minimax processing. Time spent finding these 'improvements' will be re-paid in abundance.
Putting my money where my mouth is, below is the 28-line version of one of the OP's posted 59-line function. This still uses the discrete variables for each "compass direction". Were those bundled into an array, this, too, could be written in 1/2 again as many lines.
void remove_token( struct Game *game, int player, char direction ) {
int *arr, arr_sz;
//according to the direction, remove the token and add its value to the player's score
switch( direction ) {
case 'N':
arr = game->north;
arr_sz = --game->north_size;
break;
case 'W':
arr = game->west;
arr_sz = --game->west_size;
break;
case 'E':
arr = game->east;
arr_sz = --game->east_size;
break;
case 'S':
arr = game->south;
arr_sz = --game->south_size;
break;
}
//add the chip value to the player's score
if (player == 0)
game->score_a += arr[ arr_sz ];
else
game->score_b += arr[ arr_sz ];
}

Losing data bit when reading from GPIO by libgpiod on Linux

I am using Debian (8.3.0-6) on an embedded custom board and working on the dht11 sensor.
Briefly,
I need to read 40 bits from a GPIO pin and each bit can take a max 70 microseconds. When bit-level is high for max 28us or 70us, it means is logic 0 or 1, respectively. (So I have a timeout controller for each bit and if a bit takes more than 80us, I need to stop the process.).
In my situation, sometimes I can read all 40 bits correctly but sometimes I can't do it and the function of libgpiod gpiod_line_get_value(line); is missing the bit (my code is below). I am trying to figure out that why I cant read and lose a bit, what is the reason for that. But I haven't found a sensible answer yet. So I was wondering What am I missing out?, What is the proper way to GPIO programming?
Here is what I wanted to show you, How do I understand what I am missing a bit? Whenever I catch a bit, I am setting and resetting another GPIO pin at the rising and falling edge of a bit (So I can see which bit missing). Moreover, as far as I see I am always missing the two edges on one bit or one edge on two bits consecutively (rising and falling or falling and rising). In the first picture, you can see which bit I missed, the second is when I read all bits correctly.
Here it is my code:
//********************************************************* Start reading data bit by low level (50us) ***************************
for (int i = 0; i < DHT_DATA_BYTE_COUNT ; i++) //DHT_DATA_BYTE_COUNT = 5
{
for (int J = 7; J > -1; J--)
{
GPIO_SetOutPutPin(testPin); //gpiod_line_set_value(testPin, 1);
int ret;
start = micros();
do
{
ret = GPIO_IsInputPinSet(dht11pin);//gpiod_line_get_value(dht11pin);
delta = micros() - start;
if(ret == -1)
{
err_step.step = 9;
err_step.ret_val = -1;
return -1;
}
if(delta > DHT_START_BIT_TIMEOUT_US) //80us
{
err_step.step = 10;
err_step.ret_val = -2;
err_step.timestamp[is] = delta;
err_step.indx[is].i = i;
err_step.indx[is++].j = J;
GPIO_ResetOutPutPin(testPin);
return -2;
}
}while(ret == 0);
GPIO_ResetOutPutPin(testPin);
err_step.ret_val = 10;
GPIO_SetOutPutPin(testPin);
start = micros();
do
{
ret = GPIO_IsInputPinSet(dht11pin);
delta = micros() - start;
if(ret == -1)
{
err_step.step = 11;
err_step.ret_val = -1;
return -1;
}
if(delta > DHT_BEGIN_RESPONSE_TIMEOUT_US) //80us
{
err_step.step = 12;
err_step.ret_val = -2;
err_step.timestamp[is] = delta;
err_step.indx[is].i = i;
err_step.indx[is++].j = J;
return -2;
}
}while(ret == 1);
err_step.timestamp[is] = delta;
err_step.indx[is].i = i;
err_step.indx[is++].j = J;
GPIO_ResetOutPutPin(testPin);
err_step.ret_val = 10;
(delta > DHT_BIT_SET_DATA_DETECT_TIME_US) ? bitWrite(dht11_byte[i],J,1) : bitWrite(dht11_byte[i],J,0);
}
}

Bitboard to titboard (ternary bitboard) conversion

In many board games (like checkers, go and othello/reversi) each square can be represented by three states: white, black or empty.
8x8 boards in such game engines are usually represented as two bitboards: one 64-bit integer for location of white pieces and another 64-bit integer – for black.
However, when storing local game patterns, such binary representation can require a lot of space. For example, creating a lookup table for all possible values of an 8-square row would require an array with 256*256 = 4^8 = 65536 values, compared to only 3^8 = 6561 possible positions (since a square can never be occupied by both black and white pieces).
An alternative way is to store the boards as ternary numbers – so called titboards. But I didn't find anywhere a fast algorithm to convert between two binary integers representation and ternary integer representation.
Therefore, my question is
Is there an efficient way to convert (encode) two mutually exclusive binary numbers (w & b == 0) in ternary numbers, so that each unique pair of such integers would be mapped to a resulting unique integer? (Preferably in C/C++.)
Python example
Here is my Python solution to do this:
white_black_empty = lambda w, b: int(format(w, 'b'), base=3) + \
int(format(b, 'b'), base=3)*2
Example values:
w = 10100012 = 81
b = 01001002 = 36
result = 10100013 + 01001003*2 = 10100013 + 02002003 = 12102013 = 1315
So white_black_empty(81, 36) == 1315.
But, converting integer into string representation of a binary format(x, 'b') and then from string back to integer using base 3 int(x, base=3) looks rather inefficient.
If your hardware has a fast popcount operation, then you can represent a board of n spaces as 2 n-bit values &langle;mask, colour&rangle;, where the second value is guaranteed to be in the range [0, 2popcount(mask)] The first value is 1 in the bit position corresponding to a square if the square is occupied; the second value is 1 in the bit position corresponding to j if the jth occupied square has a white piece. In order to access bits in colour, it's useful to have this function which returns a bit position in colour given mask and a bit position in the mask which corresponds to a 1-bit in the mask (i.e. a bit corresponding to an occupied square):
static inline int colourBitPos(unsigned mask, unsigned pos) {
return popcount(mask & ((1U << pos) - 1));
}
(In other words, it counts the number of one bits in mask following the specified position.)
You can then easily turn the &langle;mask, colour&rangle; pair into a single number in the range [0, 3n-1] by way of a precomputed lookup table holding base indices. When I was originally thinking of this system, I thought in terms of n+1 concatenated tables, each corresponding to a single popcount. That's conceptually nice, since the number of possible colourings of a code with popcount i is obviously 2i while the number of masks with popcount i is C(n, i) (using C() as the binomial coefficient function since there is no MathJax here). The lovely identity:
is probably less well-known than other binomial identities.
While it is possible to take advantage of that arrangement to rather laboriously compute the index in O(n) time (bit by bit in the mask field), the easiest and fastest solution is to use a 2n-element fixed lookup table base, whose size is much less than the 3n data tables. A base value is computed for each value of mask by simply accumulating the appropriate power of two for each value:
int base[1U<<N];
for (unsigned i = 0, offset = 0; i < 1U<<N; ++i) {
base[i] = offset;
offset += 1U<<popcount(i);
}
Then the index of any pair can be computed as:
index = base[mask] + colour;
[See example below]
The two-component representation is not particularly hard to work with, although it is obviously not as easy as a two-bit three-way choice. For example, to find what's in square i:
(mask & (1U << i))
? (colour & ((1U << colouredBitPos(mask, i) - 1) ? WHITE : BLACK
: EMPTY
For another example, in order to add a piece coloured col (WHITE = 1, BLACK = 0) to currently unoccupied square i, you would do:
unsigned pos = colouredBitPos(mask, i);
colour += (colour & ~((1U << pos) - 1)) + (col << pos);
mask |= 1U << i;
effectively shifting the first part of colour left one bit in order to insert the new bit. If the square were already occupied, you would avoid the shift:
unsigned pos = colouredBitPos(mask, i);
colour &= ~(1U << pos); // Make it black
colour |= col << pos; // And give it the right colour
Other operations are similarly straight-forward.
Whether that work is justified in your case will depend on so many factors that I can't possibly render a judgement. But the space overhead is close to optimal. The only overhead aside from increased code size is a single 2n-element lookup table which can be used with all of the data tables, and which is in any case tiny relative to the size of any data table (eg., for n=8, the data tables have 6561 elements so a 256-element lookup table would add 4% overhead of a single data table whose data elements are also shorts. And there is no need to persist the lookup table if you're persisting the data tables, since it can easily be regenerated.)
Index encoding example:
Using n=4 for simplicity, the base lookup table is:
mask base mask base mask base mask base
0000 0 0100 9 1000 27 1100 45
0001 1 0101 11 1001 29 1101 49
0010 3 0110 15 1010 33 1110 57
0011 5 0111 19 1011 37 1111 65
Using U=Unoccupied, B=Black, W=White (and assuming, as above, that White is 1), some example encodings and indexes:
board mask colour compute index decimal
UUBW 0011 01 base[0011]+ 01 = 6
UUWB 0011 10 base[0010]+ 10 = 7
WUBW 1011 101 base[1011]+101 = 42
How about storing what you're trying to convert? With the scheme below, each additional 8 bits of a row, would cost 512 numbers in an array (or hash table). The tradeoff would be more additions and bit-extraction to cut storage - for example, to store 8 bits, rather than the full 8, which result in 255 numbers, we could store 2^4 and 2^4 (for the second set of 4 bits), resulting in 32 (plus 32 for the blacks) numbers stored, but necessitating extracting each set of 4 bits and another addition during the conversion.
const ones = new Array(256);
const twos = new Array(256);
for (let i=0; i<256; i++){
let one = 0;
let two = 0;
for (let j=0; j<8; j++){
if ((1 << j) & i){
one += Math.pow(3, j);
two += 2*Math.pow(3, j);
}
ones[i] = one;
twos[i] = two;
}
}
function convert(w, b){
return ones[w] + twos[b];
}
console.log(convert(81, 36));
Converting from string to integer and back will indeed be inefficient.
If you just need to encode the values, thinking of them in terms of the actual numbers they represent will be useful. For example, in considering eight rows on a board, the first position's state is effectively boardState % 3; we can use the convention that a black piece is there on a 1, a white piece on a 2, and an empty value on a 0. For the second, it becomes (boardState % 9)/3, the third (boardState % 27) / 3 and so on.
So, for encoding, we can extend this thinking: we take either a 0, 1, or 2, multiply it by 3 to the power of (whichever board position we're considering), and add it to some "result" number. Some (VERY untested) example code is below:
#include <inttypes.h>
#include <math.h>
uint64_t tritboard(uint64_t white, uint64_t black){
uint64_t onemask = 0x0000000000000001;//you could also just say "= 1"
uint64_t retval = 0;
uint64_t thisPos;
for(char i = 0; i < 8; i++){
thisPos = 0;
if(white & (oneMask << i)) thisPos += 2;
if(black & (oneMask << i)) thisPos += 1;
retval += thisPos * ( (uint64_t) pow(3, i));
}//for
return retval;
}//tritboard
Unfortunately, with computers being partial to binary, you're only going to be able to get but so clever about bitshifts. Thus, the for loop in this code(which is slightly less gross in C as it is in python, in terms of performance).
Note that you are limited in scope for this approach; as you can appreciate, you can't represent the entire board with this approach (as there aren't 3^64 possible values for a 64-bit integer).
Hopefully, that is more amenable to you than the string approach!
In practice, you'll want to store the board state in base-4 packed in unsigned longs, with each board row padded to an integral number of unsigned longs. This will give you the best memory locality, very fast access to board cells, but uses 26.2% more RAM than ternary packing.
To store the board state in a binary file, you can pack 5 ternary digits (five board cell states) into each 8-bit byte. This uses only 5.1% more memory than ternary packing, and is simple and robust to implement. In particular, this way you do not need to worry about byte order (endianness).
The problem with pure ternary packing is that each base-3 digit affects most of the binary digits representing the same numerical value. For example, 38 = 300000003 = 6561 = 11001101000012. This means that the only practical way to extract base-3 digits is via repeated division and modulus (by 3).
To describe a board of size N×M, the ternary packing and unpacking function will be essentially O(N2M2), and therefore slower and slower as the board size increases. You'll likely get better savings by using a compression library (say, liblzma) using less CPU time. For many board configurations, run-length encoding might also work well.
Here is an example implementation for boards of up to 16777215×16777215 cells (tested only up to 32768×32768 cells):
#include <stdlib.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <time.h>
#define ULONG_BITS (CHAR_BIT * sizeof (unsigned long))
#define ULONG_CELLS (CHAR_BIT * sizeof (unsigned long) / 2)
struct board {
int rows;
int cols;
size_t stride;
unsigned long *data;
};
enum {
EMPTY = 0, /* calloc() clears the data to zeroes */
WHITE = 1,
BLACK = 2,
ERROR = 3
};
int board_init(struct board *const b, const int rows, const int cols)
{
const size_t stride = (cols + ULONG_CELLS - 1) / ULONG_CELLS;
const size_t ulongs = stride * (size_t)rows;
if (b) {
b->rows = 0;
b->cols = 0;
b->stride = 0;
b->data = NULL;
}
if (!b || rows < 1 || cols < 1)
return -1;
if ((size_t)(ulongs / stride) != (size_t)rows)
return -1;
b->data = calloc(ulongs, sizeof b->data[0]);
if (!b->data)
return -1;
b->rows = rows;
b->cols = cols;
b->stride = stride;
return 0;
}
static inline int get_cell(const struct board *const b, const int row, const int col)
{
if (!b || row < 0 || col < 0 || row >= b->rows || col >= b->cols)
return EMPTY;
else {
const size_t i = (size_t)col / ULONG_CELLS;
const size_t c = ((size_t)col % ULONG_CELLS) * 2;
const unsigned long w = b->data[b->stride * row + i];
return (w >> c) & 3;
}
}
static inline int set_cell(struct board *const b, const int row, const int col, const int value)
{
if (!b || row < 0 || col < 0 || row >= b->rows || col >= b->cols)
return EMPTY;
else {
const size_t i = (size_t)col / ULONG_CELLS;
const size_t c = ((size_t)col % ULONG_CELLS) * 2;
unsigned long *w = b->data + b->stride * row + i;
*w = ((*w) & (3uL << c)) | ((unsigned long)(value & 3) << c);
return value & 3;
}
}
static inline int write_u24(FILE *const out, const int value)
{
unsigned int u = value;
if (!out || value < 0 || value > 16777215 || ferror(out))
return -1;
if (fputc(u & 255, out) == EOF)
return -1;
else
u >>= 8;
if (fputc(u & 255, out) == EOF)
return -1;
else
u >>= 8;
if (fputc(u & 255, out) == EOF)
return -1;
else
return 0;
}
static inline int read_u24(FILE *const in, unsigned int *const to)
{
unsigned int result;
int c;
if (!in || ferror(in))
return -1;
c = fgetc(in);
if (c == EOF)
return -1;
else
result = c & 255;
c = fgetc(in);
if (c == EOF)
return -1;
else
result |= (c & 255) << 8;
c = fgetc(in);
if (c == EOF)
return -1;
else
result |= (c & 255) << 16;
if (to)
*to = result;
return 0;
}
int board_save(const struct board *const b, FILE *const out)
{
int row, col, cache, coeff;
if (!b || !out || ferror(out) || !b->stride ||
b->rows < 1 || b->rows > 16777215 ||
b->cols < 1 || b->cols > 16777215)
return -1;
if (write_u24(out, b->rows))
return -1;
if (write_u24(out, b->cols))
return -1;
/* Clear byte cache. */
cache = 0;
coeff = 1;
for (row = 0; row < b->rows; row++) {
for (col = 0; col < b->cols; col++) {
switch (get_cell(b, row, col)) {
case EMPTY: /* Saved as 0 */
break;
case WHITE: /* Saved as 1 */
cache += coeff;
break;
case BLACK: /* Saved as 2 */
cache += coeff + coeff;
break;
default: /* Invalid cell state. */
return -1;
}
if (coeff >= 81) {
if (fputc(cache, out) == EOF)
return -1;
cache = 0;
coeff = 1;
} else
coeff *= 3;
}
}
if (coeff > 1)
if (fputc(cache, out) == EOF)
return -1;
if (fflush(out))
return -1;
return 0;
}
int board_load(struct board *const b, FILE *in)
{
unsigned int rows, cols, row, col, cache, count;
int c;
if (b) {
b->rows = 0;
b->cols = 0;
b->stride = 0;
b->data = NULL;
}
if (!b || !in || ferror(in))
return -1;
if (read_u24(in, &rows) || rows < 1 || rows > 16777215)
return -1;
if (read_u24(in, &cols) || cols < 1 || cols > 16777215)
return -1;
if (board_init(b, rows, cols))
return -1;
/* Nothing cached at this point. */
cache = 0;
count = 0;
for (row = 0; row < rows; row++) {
for (col = 0; col < cols; col++) {
if (count < 1) {
c = fgetc(in);
if (c == EOF || c < 0 || c >= 243)
return -1;
cache = c;
count = 5;
}
switch (cache % 3) {
case 0: /* Leave as background. */
break;
case 1: /* White */
if (set_cell(b, row, col, WHITE) != WHITE)
return -1;
break;
case 2: /* Black */
if (set_cell(b, row, col, BLACK) != BLACK)
return -1;
break;
}
cache /= 3;
count--;
}
}
/* No errors. */
return 0;
}
/* Xorshift 64* pseudo-random number generator. */
static uint64_t prng_state = 1;
static inline uint64_t prng_randomize(void)
{
int rounds = 1024;
uint64_t state;
state = (uint64_t)time(NULL);
while (rounds-->0) {
state ^= state >> 12;
state ^= state << 25;
state ^= state >> 27;
}
if (!state)
state = 1;
prng_state = state;
return state;
}
static inline uint64_t prng_u64(void)
{
uint64_t state = prng_state;
state ^= state >> 12;
state ^= state << 25;
state ^= state >> 27;
prng_state = state;
return state * UINT64_C(2685821657736338717);
}
/* Uniform random ternary generator. */
static uint64_t ternary_cache = 0;
static int ternary_bits = 0;
static inline int prng_ternary(void)
{
int retval;
do {
if (ternary_bits < 2) {
ternary_cache = prng_u64();
ternary_bits = 64;
}
retval = ternary_cache & 3;
ternary_cache >>= 1;
ternary_bits -= 2;
} while (retval > 2);
return retval;
}
int main(int argc, char *argv[])
{
struct board original, reloaded;
uint64_t correct, incorrect, count[3];
double percent;
FILE *file;
int rows, cols, row, col;
char dummy;
if (argc != 4) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s FILENAME ROWS COLUMNS\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "This program generates a random ternary board,\n");
fprintf(stderr, "saves it to file FILENAME, reads it back, and\n");
fprintf(stderr, "verifies that the board state is intact.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
if (!argv[1][0]) {
fprintf(stderr, "No filename specified.\n");
return EXIT_FAILURE;
}
if (sscanf(argv[2], "%d %c", &rows, &dummy) != 1 || rows < 1 || rows > 16777215) {
fprintf(stderr, "%s: Invalid number of rows.\n", argv[2]);
return EXIT_FAILURE;
}
if (sscanf(argv[3], "%d %c", &cols, &dummy) != 1 || cols < 1 || cols > 16777215) {
fprintf(stderr, "%s: Invalid number of columns.\n", argv[2]);
return EXIT_FAILURE;
}
if (board_init(&original, rows, cols)) {
fprintf(stderr, "Cannot create a board with %d rows and %d columns.\n", rows, cols);
return EXIT_FAILURE;
}
fprintf(stderr, "Filling board with a random state; random seed is %" PRIu64 ".\n", prng_randomize());
percent = 100.0 / (double)rows / (double)cols;
count[0] = count[1] = count[2] = 0;
for (row = 0; row < rows; row++)
for (col = 0; col < cols; col++) {
int t = prng_ternary();
if (t < 0 || t > 3) {
fprintf(stderr, "prng_ternary() returned %d!\n", t);
return EXIT_FAILURE;
}
count[t]++;
set_cell(&original, row, col, t);
}
fprintf(stderr, " Empty: %" PRIu64 " cells, %.3f%%.\n", count[EMPTY], (double)count[EMPTY] * percent);
fprintf(stderr, " White: %" PRIu64 " cells, %.3f%%.\n", count[WHITE], (double)count[WHITE] * percent);
fprintf(stderr, " Black: %" PRIu64 " cells, %.3f%%.\n", count[BLACK], (double)count[BLACK] * percent);
file = fopen(argv[1], "wb");
if (!file) {
fprintf(stderr, "%s: Cannot open file for writing.\n", argv[1]);
return EXIT_FAILURE;
}
fprintf(stderr, "Saving to %s.\n", argv[1]);
if (board_save(&original, file)) {
fclose(file);
fprintf(stderr, "Write error.\n");
return EXIT_FAILURE;
}
if (fclose(file)) {
fprintf(stderr, "Write error.\n");
return EXIT_FAILURE;
}
fprintf(stderr, "Reloading game board.\n");
file = fopen(argv[1], "rb");
if (!file) {
fprintf(stderr, "%s: Cannot open file for reading.\n", argv[1]);
return EXIT_FAILURE;
}
if (board_load(&reloaded, file)) {
fclose(file);
fprintf(stderr, "Read error.\n");
return EXIT_FAILURE;
}
if (fclose(file)) {
fprintf(stderr, "Read error.\n");
return EXIT_FAILURE;
}
if (original.rows != reloaded.rows) {
fprintf(stderr, "Row count mismatches.\n");
return EXIT_FAILURE;
} else
if (original.cols != reloaded.cols) {
fprintf(stderr, "Column count mismatches.\n");
return EXIT_FAILURE;
}
fprintf(stderr, "Comparing board states.\n");
correct = 0;
incorrect = 0;
for (row = 0; row < rows; row++)
for (col = 0; col < cols; col++)
if (get_cell(&original, row, col) == get_cell(&reloaded, row, col))
correct++;
else
incorrect++;
if (incorrect) {
fprintf(stderr, "Found %" PRIu64 " mismatching cells (%.3f%%).\n", incorrect, (double)incorrect * percent);
return EXIT_FAILURE;
}
if (correct != (uint64_t)((uint64_t)rows * (uint64_t)cols)) {
fprintf(stderr, "Internal bug in the board comparison double loop.\n");
return EXIT_FAILURE;
}
fprintf(stderr, "Verification successful; functions work as expected for a board with %d rows and %d columns.\n", rows, cols);
return EXIT_SUCCESS;
}
The board_init() function initializes a board, board_save() saves a board state to a stream, including the board size, in portable binary format (each file will generate the same board on both big-endian and little-endian architectures), and board_load() will load a previously saved board from a stream. They all return 0 if success, nonzero if error.
The get_cell() and set_cell() functions are static inline accessor functions to examine and set the state of individual cells in a board.
As I initially suggested, this one uses 2 bits per cell in RAM (4 cells per byte), and 5 cells per byte when stored to a file.
The example program takes three command-line parameters: a file name, the number of rows, and the number of columns. It will generate a random state of that size, save it to the named file, read it back from the named file into a separate board, and finally compare the board states, to verify if the implemented functions seem to work correctly.

Efficient way to find divisibility

Professor says this isn't a efficient algorithm to check whether the number is divisible by a number from 100,000-150,000. I'm having trouble finding a better way. Any help would be appreciated.
unsigned short divisibility_check(unsigned long n) {
unsigned long i;
for (i = 100000; i <= 150000; i++) {
if (n % i == 0) {
return 0;
}
}
return 1;
}
Let's say you need to find whether a positive integer K is divisible by a number between 100,000 and 150,000, and it is such a rare operation, that doing precalculations is just not worth the processor time or memory used.
If K < 100,000, it cannot be divisible by a number between 100,000 and 150,000.
If 100,000 ≤ K ≤ 150,000, it is divisible by itself. It is up to you to decide whether this counts or not.
For a K > 150,000 to be divisible by M, with 100,000 ≤ M ≤ 150,000, K must also be divisible by L = K / M. This is because K = L × M, and all three are positive integers. So, you only need to test the divisibility of K by a set of L, where ⌊ K / 150,000 ⌋ ≤ L ≤ ⌊ K / 100,000 ⌋.
However, that set of Ls becomes larger than the set of possible Ms when K > = 15,000,000,000. Then it is again less work to just test K for divisibility against each M, much like OP's code is now.
When implementing this as a program, the most important thing in practice is, surprisingly, the comments you add. Do not write comments that describe what the code does; write comments that explain the model or algorithm you are trying to implement (say, at the function level), and your intent of what each small block of code should accomplish.
In this particular case, you should probably add a comment to each if clause, explaining your reasoning, much like I did above.
Beginner programmers often omit comments completely. It is unfortunate, because writing good comments is a hard habit to pick up afterwards. It is definitely a good idea to learn to comment your code (as I described above -- the comments that describe what the code does are less than useful; more noise than help), and keep honing your skill on that.
A programmer whose code is maintainable, is worth ten geniuses who produce write-only code. This is because all code has bugs, because humans make errors. To be an efficient developer, your code must be maintainable. Otherwise you're forced to rewrite each buggy part from scratch, wasting a lot of time. And, as you can see above, "optimization" at the algorithmic level, i.e. thinking about how to avoid having to do work, yields much better results than trying to optimize your loops or something like that. (You'll find in real life that surprisingly often, optimizing a loop in the proper way, removes the loop completely.)
Even in exercises, proper comments may be the difference between "no points, this doesn't work" and "okay, I'll give you partial credit for this one, because you had a typo/off-by-one bug/thinko on line N, but otherwise your solution would have worked".
As bolov did not understand how the above leads to a "naive_with_checks" function, I'll show it implemented here.
For ease of testing, I'll show a complete test program. Supply the range of integers to test, and the range of divisors accepted, as parameters to the program (i.e. thisprogram 1 500000 100000 150000 to duplicate bolov's tests).
#include <stdlib.h>
#include <inttypes.h>
#include <limits.h>
#include <locale.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
int is_divisible(const uint64_t number,
const uint64_t minimum_divisor,
const uint64_t maximum_divisor)
{
uint64_t divisor, minimum_result, maximum_result, result;
if (number < minimum_divisor) {
return 0;
}
if (number <= maximum_divisor) {
/* Number itself is a valid divisor. */
return 1;
}
minimum_result = number / maximum_divisor;
if (minimum_result < 2) {
minimum_result = 2;
}
maximum_result = number / minimum_divisor;
if (maximum_result < minimum_result) {
maximum_result = minimum_result;
}
if (maximum_result - minimum_result > maximum_divisor - minimum_divisor) {
/* The number is so large that it is the least amount of work
to check each possible divisor. */
for (divisor = minimum_divisor; divisor <= maximum_divisor; divisor++) {
if (number % divisor == 0) {
return 1;
}
}
return 0;
} else {
/* There are fewer possible results than divisors,
so we check the results instead. */
for (result = minimum_result; result <= maximum_result; result++) {
if (number % result == 0) {
divisor = number / result;
if (divisor >= minimum_divisor && divisor <= maximum_divisor) {
return 1;
}
}
}
return 0;
}
}
int parse_u64(const char *s, uint64_t *to)
{
unsigned long long value;
const char *end;
/* Empty strings are not valid. */
if (s == NULL || *s == '\0')
return -1;
/* Parse as unsigned long long. */
end = s;
errno = 0;
value = strtoull(s, (char **)(&end), 0);
if (errno == ERANGE)
return -1;
if (end == s)
return -1;
/* Overflow? */
if (value > UINT64_MAX)
return -1;
/* Skip trailing whitespace. */
while (isspace((unsigned char)(*end)))
end++;
/* If the string does not end here, it has garbage in it. */
if (*end != '\0')
return -1;
if (to)
*to = (uint64_t)value;
return 0;
}
int main(int argc, char *argv[])
{
uint64_t kmin, kmax, dmin, dmax, k, count;
if (argc != 5) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help | help ]\n", argv[0]);
fprintf(stderr, " %s MIN MAX MIN_DIVISOR MAX_DIVISOR\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "This program counts which positive integers between MIN and MAX,\n");
fprintf(stderr, "inclusive, are divisible by MIN_DIVISOR to MAX_DIVISOR, inclusive.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
/* Use current locale. This may change which codes isspace() considers whitespace. */
if (setlocale(LC_ALL, "") == NULL)
fprintf(stderr, "Warning: Your C library does not support your current locale.\n");
if (parse_u64(argv[1], &kmin) || kmin < 1) {
fprintf(stderr, "%s: Invalid minimum positive integer to test.\n", argv[1]);
return EXIT_FAILURE;
}
if (parse_u64(argv[2], &kmax) || kmax < kmin || kmax >= UINT64_MAX) {
fprintf(stderr, "%s: Invalid maximum positive integer to test.\n", argv[2]);
return EXIT_FAILURE;
}
if (parse_u64(argv[3], &dmin) || dmin < 2) {
fprintf(stderr, "%s: Invalid minimum divisor to test for.\n", argv[3]);
return EXIT_FAILURE;
}
if (parse_u64(argv[4], &dmax) || dmax < dmin) {
fprintf(stderr, "%s: Invalid maximum divisor to test for.\n", argv[4]);
return EXIT_FAILURE;
}
count = 0;
for (k = kmin; k <= kmax; k++)
count += is_divisible(k, dmin, dmax);
printf("%" PRIu64 "\n", count);
return EXIT_SUCCESS;
}
It is useful to note that the above, running bolov's test, i.e. thisprogram 1 500000 100000 150000 only takes about 15 ms of wall clock time (13 ms CPU time), median, on a much slower Core i5-7200U processor. For really large numbers, like 280,000,000,000 to 280,000,010,000, the test does the maximum amount of work, and takes about 3.5 seconds per 10,000 numbers on this machine.
In other words, I wouldn't trust bolov's numbers to have any relation to timings for properly written test cases.
It is important to note that for any K between 1 and 500,000, the same test that bolov says their code measures, the above code does at most two divisibility tests to find if K is divisible by an integer between 100,000 and 150,000.
This solution is therefore quite efficient. It is definitely acceptable and near-optimal, when the tested K are relatively small (say, 32 bit unsigned integers or smaller), or when precomputed tables cannot be used.
Even when precomputed tables can be used, it is unclear if/when prime factorization becomes faster than the direct checks. There is certainly a tradeoff in the size and content of the precomputed tables. bolov claims that it is clearly superior to other methods, but hasn't implemented a proper "naive" divisibility test as shown above, and bases their opinion on experiments on quite small integers (1 to 500,000) that have simple prime decompositions.
As an example, a table of integers 1 to 500,000 pre-checked for divisibility takes only 62500 bytes (43750 bytes for 150,000 to 500,000). With that table, each test takes a small near-constant time (that only depends on memory and cache effects). Extending it to all 32-bit unsigned integers would require 512 GiB (536,870,912 bytes); the table can be stored in a memory-mapped read-only file, to let the OS kernel manage how much of it is mapped to RAM at any time.
Prime decomposition itself, especially using trial division, becomes more expensive than the naive approach when the number of trial divisions exceeds the range of possible divisors (50,000 divisors in this particular case). As there are 13848 primes (if one counts 1 and 2 as primes) between 1 and 150,000, the number of trial divisions can easily approach the number of divisors for sufficiently large input values.
For numbers with many prime factors, the combinatoric phase, finding if any subset of the prime factors multiply to a number between 100,000 and 150,000 is even more problematic. The number of possible combinations grows faster than exponentially. Without careful checks, this phase alone can do way more work per large input number than just trial division with each possible divisor would be.
(As an example, if you have 16 different prime factors, you already have 65,535 different combinations; more than the number of direct trial divisions. However, all such numbers are larger than 64-bit; the smallest being 2·3·5·7·11·13·17·19·23·29·31·37·41·43·47·53 = 32,589,158,477,190,044,730 which is a 65-bit number.)
There is also the problem of code complexity. The more complex the code, the harder it is to debug and maintain.
Ok, so I've implemented the version with sieve primes and factorization mentioned in the comments by m69 and it is ... way faster than the naive approach. I must admit, I didn't expect this at all.
My notations: left == 100'000 and right = 150'000
naive your version
naive_with_checks your version with simple checks:
if (n < left) no divisor
else if (n <= right) divisor
else if (left * 2 >= right && n < left * 2) divisor
factorization (above checks implemented)
Precompute the Sieve of Eratosthenes for all primes up to right. This time is not measured
factorize n (only with the primes from the prev step)
generate all subsets (backtracking, depth first: i.e. generate p1^0 * p2^0 * p3^0 first, instead of p1^5 first) with the product < left or until the product is in [left, right] (found divisor).
factorization_opt optimization of the previous algorithm where the subsets are not generated (no vector of subsets is created). I just pass the current product from one backtracking iteration to the next.
Nominal Animal's version I have also ran his version on my system with the same range.
I have written the program in C++ so I won't share it here.
I used std::uint64_t as data type and I have checked all numbers from 1 to 500'000 to see if each is divisible by a number in interval [100'000, 150'000]. All version reached the same solution: 170'836 numbers with positive results.
The setup:
Hardware: Intel Core i7-920, 4 cores with HT (all algorithm versions are single threaded), 2.66 GHz (boost 2.93 GHz),
8 MB SmartCache; memory: 6 GB DDR3 triple channel.
Compiler: Visual Studio 2017 (v141), Release x64 mode.
I must also add that I haven't profiled the programs so there is definitely room to improve the implementation. However this is enough here as the idea is to find a better algorithm.
version | elapsed time (milliseconds)
-----------------------+--------------
naive         |  167'378 ms (yes, it's thousands separator, aka 167 seconds)
naive_with_checks |   97'197 ms
factorization | 7'906 ms
factorization_opt | 7'320 ms
|
Nominal Animal version | 14 ms
Some analysis:
For naive vs naive_with_checks: all the numbers in [1 200'000] can be solved with just the simple checks. As these represent 40% of all the numbers checked, the naive_with_checks version does roughly 60% of the work naive does. The execution time reflect this as naive_with_checks runtime is ≅58% of the naive version.
The factorization version is a whopping 12.3 times faster. That is indeed impressive. I haven't analyzed the time complexity of the alg.
And the final optimization brings a further 1.08x speedup. This is basically the time gained by removing the creation and copy of the small vectors of subset factors.
For those interested the sieve precomputation which is not included above takes about 1 ms. And this is the naive implementation from wikipedia, no optimizations whatsoever.
For comparison, here's what I had in mind when I posted my comment about using prime factorization. Compiled with gcc -std=c99 -O3 -m64 -march=haswell this is slightly faster than the naive method with checks and inversion when tested with the last 10,000 integers in the 64-bit range (3.469 vs 3.624 seconds).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
void eratosthenes(bool *ptr, uint64_t size) {
memset(ptr, true, size);
for (uint64_t i = 2; i * i < size; i++) {
if (ptr[i]) {
for (uint64_t j = i * i; j < size; j += i) {
ptr[j] = false;
}
}
}
}
bool divisible(uint64_t n, uint64_t a, uint64_t b) {
/* check for trivial cases first */
if (n < a) {
return false;
}
if (n <= b) {
return true;
}
if (n < 2 * a) {
return false;
}
/* Inversion: use range n/b ~ n/a; see Nominal Animal's answer */
if (n < a * b) {
uint64_t c = a;
a = (n + b - 1) / b; // n/b rounded up
b = n / c;
}
/* Create prime sieve when first called, or re-calculate it when */
/* called with a higher value of b; place before inversion in case */
/* of a large sequential test, to avoid repeated re-calculation. */
static bool *prime = NULL;
static uint64_t prime_size = 0;
if (prime_size <= b) {
prime_size = b + 1;
prime = realloc(prime, prime_size * sizeof(bool));
if (!prime) {
printf("Out of memory!\n");
return false;
}
eratosthenes(prime, prime_size);
}
/* Factorize n into prime factors up to b, using trial division; */
/* there are more efficient but also more complex ways to do this. */
/* You could return here, if a factor in the range a~b is found. */
static uint64_t factor[63];
uint8_t factors = 0;
for (uint64_t i = 2; i <= n && i <= b; i++) {
if (prime[i]) {
while (n % i == 0) {
factor[factors++] = i;
n /= i;
}
}
}
/* Prepare divisor sieve when first called, or re-allocate it when */
/* called with a higher value of b; in a higher-level language, you */
/* would probably use a different data structure for this, because */
/* this method iterates repeatedly over a potentially sparse array. */
static bool *divisor = NULL;
static uint64_t div_size = 0;
if (div_size <= b / 2) {
div_size = b / 2 + 1;
divisor = realloc(divisor, div_size * sizeof(bool));
if (!divisor) {
printf("Out of memory!\n");
return false;
}
}
memset(divisor, false, div_size);
divisor[1] = true;
uint64_t max = 1;
/* Iterate over each prime factor, and for every divisor already in */
/* the sieve, add the product of the divisor and the factor, up to */
/* the value b/2. If the product is in the range a~b, return true. */
for (uint8_t i = 0; i < factors; i++) {
for (uint64_t j = max; j > 0; j--) {
if (divisor[j]) {
uint64_t product = factor[i] * j;
if (product >= a && product <= b) {
return true;
}
if (product < div_size) {
divisor[product] = true;
if (product > max) {
max = product;
}
}
}
}
}
return false;
}
int main() {
uint64_t count = 0;
for (uint64_t n = 18446744073709541615LLU; n <= 18446744073709551614LLU; n++) {
if (divisible(n, 100000, 150000)) ++count;
}
printf("%llu", count);
return 0;
}
And this is the naive + checks + inversion implementation I compared it with:
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
bool divisible(uint64_t n, uint64_t a, uint64_t b) {
if (n < a) {
return false;
}
if (n <= b) {
return true;
}
if (n < 2 * a) {
return false;
}
if (n < a * b) {
uint64_t c = a;
a = (n + b - 1) / b;
b = n / c;
}
while (a <= b) {
if (n % a++ == 0) return true;
}
return false;
}
int main() {
uint64_t count = 0;
for (uint64_t n = 18446744073709541615LLU; n <= 18446744073709551614LLU; n++) {
if (divisible(n, 100000, 150000)) ++count;
}
printf("%llu", count);
return 0;
}
Here's a recursive method with primes. The idea here is that if a number is divisible by a number between 100000 and 150000, there is a path of reducing by division the product of only relevant primes that will pass through a state in the target range. (Note: the code below is meant for numbers greater than 100000*150000). In my testing, I could not find an instance where the stack performed over 600 iterations.
# Euler sieve
def getPrimes():
n = 150000
a = (n+1) * [None]
ps = ([],[])
s = []
p = 1
while (p < n):
p = p + 1
if not a[p]:
s.append(p)
# Save primes less
# than half
# of 150000, the only
# ones needed to construct
# our candidates.
if p < 75000:
ps[0].append(p);
# Save primes between
# 100000 and 150000
# in case our candidate
# is prime.
elif p > 100000:
ps[1].append(p)
limit = n / p
new_s = []
for i in s:
j = i
while j <= limit:
new_s.append(j)
a[j*p] = True
j = j * p
s = new_s
return ps
ps1, ps2 = getPrimes()
def f(n):
# Prime candidate
for p in ps2:
if not (n % p):
return True
# (primes, prime_counts)
ds = ([],[])
prod = 1
# Prepare only prime
# factors that could
# construct a composite
# candidate.
for p in ps1:
while not (n % p):
prod *= p
if (not ds[0] or ds[0][-1] != p):
ds[0].append(p)
ds[1].append(1)
else:
ds[1][-1] += 1
n /= p
# Reduce the primes product to
# a state where it's between
# our target range.
stack = [(prod,0)]
while stack:
prod, i = stack.pop()
# No point in reducing further
if prod < 100000:
continue
# Exit early
elif prod <= 150000:
return True
# Try reducing the product
# by different prime powers
# one prime at a time
if i < len(ds[0]):
for p in xrange(ds[1][i] + 1):
stack.append((prod / ds[0][i]**p, i + 1))
return False
Output:
c = 0
for ii in xrange(1099511627776, 1099511628776):
f_i = f(ii)
if f_i:
c += 1
print c # 239
Here is a very simple solution with a sieve cache. If you call the divisibility_check function for many numbers in a sequence, this should be very efficient:
#include <string.h>
int divisibility_check_sieve(unsigned long n) {
static unsigned long sieve_min = 1, sieve_max;
static unsigned char sieve[1 << 19]; /* 1/2 megabyte */
if (n < sieve_min || n > sieve_max) {
sieve_min = n & ~(sizeof(sieve) - 1);
sieve_max = sieve_min + sizeof(sieve) - 1;
memset(sieve, 1, sizeof sieve);
for (unsigned long m = 100000; m <= 150000; m++) {
unsigned long i = sieve_min % m;
if (i != 0)
i = m - i;
for (; i < sizeof sieve; i += m) {
sieve[i] = 0;
}
}
}
return sieve[n - sieve_min];
}
Here is a comparative benchmark:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int divisibility_check_naive(unsigned long n) {
for (unsigned long i = 100000; i <= 150000; i++) {
if (n % i == 0) {
return 0;
}
}
return 1;
}
int divisibility_check_small(unsigned long n) {
unsigned long i, min = n / 150000, max = n / 100000;
min += (min == 0);
max += (max == 0);
if (max - min > 150000 - 100000) {
for (i = 100000; i <= 150000; i++) {
if (n % i == 0) {
return 0;
}
}
return 1;
} else {
for (i = min; i <= max; i++) {
if (n % i == 0) {
unsigned long div = n / i;
if (div >= 100000 && div <= 150000)
return 0;
}
}
return 1;
}
}
int divisibility_check_sieve(unsigned long n) {
static unsigned long sieve_min = 1, sieve_max;
static unsigned char sieve[1 << 19]; /* 1/2 megabyte */
if (n < sieve_min || n > sieve_max) {
sieve_min = n & ~(sizeof(sieve) - 1);
sieve_max = sieve_min + sizeof(sieve) - 1;
memset(sieve, 1, sizeof sieve);
for (unsigned long m = 100000; m <= 150000; m++) {
unsigned long i = sieve_min % m;
if (i != 0)
i = m - i;
for (; i < sizeof sieve; i += m) {
sieve[i] = 0;
}
}
}
return sieve[n - sieve_min];
}
int main(int argc, char *argv[]) {
unsigned long n, count = 0, lmin, lmax, range[2] = { 1, 500000 };
int pos = 0, naive = 0, small = 0, sieve = 1;
clock_t t;
char *p;
for (int i = 1; i < argc; i++) {
n = strtoul(argv[i], &p, 0);
if (*p == '\0' && pos < 2)
range[pos++] = n;
else if (!strcmp(argv[i], "naive"))
naive = 1;
else if (!strcmp(argv[i], "small"))
small = 1;
else if (!strcmp(argv[i], "sieve"))
sieve = 1;
else
printf("invalid argument: %s\n", argv[i]);
}
lmin = range[0];
lmax = range[1] + 1;
if (naive) {
t = clock();
for (count = 0, n = lmin; n != lmax; n++) {
count += divisibility_check_naive(n);
}
t = clock() - t;
printf("naive: [%lu..%lu] -> %lu non-divisible numbers, %10.2fms\n",
lmin, lmax - 1, count, t * 1000.0 / CLOCKS_PER_SEC);
}
if (small) {
t = clock();
for (count = 0, n = lmin; n != lmax; n++) {
count += divisibility_check_small(n);
}
t = clock() - t;
printf("small: [%lu..%lu] -> %lu non-divisible numbers, %10.2fms\n",
lmin, lmax - 1, count, t * 1000.0 / CLOCKS_PER_SEC);
}
if (sieve) {
t = clock();
for (count = 0, n = lmin; n != lmax; n++) {
count += divisibility_check_sieve(n);
}
t = clock() - t;
printf("sieve: [%lu..%lu] -> %lu non-divisible numbers, %10.2fms\n",
lmin, lmax - 1, count, t * 1000.0 / CLOCKS_PER_SEC);
}
return 0;
}
Here are some run times:
naive: [1..500000] -> 329164 non-divisible numbers, 158174.52ms
small: [1..500000] -> 329164 non-divisible numbers, 12.62ms
sieve: [1..500000] -> 329164 non-divisible numbers, 1.35ms
sieve: [0..4294967295] -> 3279784841 non-divisible numbers, 8787.23ms
sieve: [10000000000000000000..10000000001000000000] -> 765978176 non-divisible numbers, 2205.36ms

how to find max value with less iterations

I am changing the phase of signal from 0 to 360 by each degree to get max voltage value.Because if i change phase of the signal the voltage also changes.I have the fallowing code to find max value.
void Maxphase(float *max, unsigned int *index)
{
*max = 0.0;
float value;
unsigned int i, data;
for (i=0;i<=360;i++)
{
phaseset(i);
delay_ms(100);
data = readvalue();
value = voltage(mux1);
if(value > *max) //find max value
{
*max = value; //max voltage
*index = i;
}
}
}
from the above code I am getting Max value(voltage) after 38 sec(360*100) because for every read operation my device needs 100ms delay. This is too large, I can't change hardware thus i want to get the max value within 2 to 3 sec by optimizing software.
then I have tried with the fallowing code.
void Maxphase(float *max1, unsigned int *index1)
{
max = 0.0;
float value;
unsigned int i,j,data;
for (i=0;i<=360;i+=10)
{
phaseset(i);
delay_ms(100);
data = readvalue();
value = voltage(mux1);
if(value > max) //find max value
{
max = value; //max voltage
index = i;
}
}
*max1=max;
*index1=index;
for (i=*index1-9;i<=*index1+9;i+=1)
{
j=i;
phaseset(j);
delay_ms(100);
data = readvalue();
value = voltage(mux1);
if(value > *max1) //find max value
{
*max1 = value; //max voltage
*index1 = i;
}
}
}
I have reduced time from 45 sec to 7 sec. i have reduced iterations 360 to 54(54*100). I want to reduce it 7 sec to 2 sec.
Can any one help me with better algorithm that i can get max value from (0 to 360) with in 2 sec.
I have measured the voltage values using scope by changing phase. I have written below how it vary voltage with phase.
Phase (degree) voltage(max)
0 0.9mv
45 9.5mv
90 9.0mv
135 0.9mv
180 292mv
225 601mv
270 555mv
315 230mv
360 0.9mv
I am new to C programming. Can anyone provide sample code for the best algorithm.
Golden section search is probably what you are after. It is effective, but still pretty simple.
If you want something even faster and more sophisticated, you can use Brent's method.
If you can be sure that there is only a single highest point on your 360 degrees you can do a recursive divide and conquer.
You start by looking e.g. at 0, 180, 270. Let's say you find the answer is that 180 + 270 together have the highest value. Than you start by looking in at 210.... Which side is higher? And so on ...
Exploiting the various comments and suggestions here, I present this untested piece of code. I don't know whether this works at all or is an improvement over the existing source, but it was fun to try, anyway:
extern void phaseset(int);
extern void delay_ms(int);
extern float readvalue();
extern float voltage(int);
extern int mux1;
float probe(int phase)
{
float data;
phaseset(phase);
delay_ms(100);
data = readvalue(); /* data is ignored? */
return voltage(mux1); /* mux1? */
}
/* helper routine, find the max in a given range [phase1, phase2] */
void maxphase_aux(int phase1, float vol1, int phase2, float vol2, int *phaseret, float *volret)
{
float xvol1 = 0, xvol2 = 0;
int xphase1 = -1, xphase2 = -1;
/* test the voltage in the middle */
int phasem = abs(phase2 - phase1) / 2;
float volm = probe(phasem);
if (volm > vol1 && volm > vol2) {
/* middle point is the highest so far,
* search left and right for maximum */
*volret = volm;
*phaseret = phasem;
maxphase_aux(phase1, vol1, phasem, volm, &xphase1, &xvol1);
maxphase_aux(phase2, vol2, phasem, volm, &xphase2, &xvol2);
} else if (volm < vol1 && volm > vol2) {
/* vol1 is the highest so far,
* search between volm and vol1 for maximum */
maxphase_aux(phase1, vol1, phasem, volm, &xphase1, &xvol1);
} else if (volm > vol1 && volm < vol2) {
/* vol2 is the highest so far,
* search between volm and vol2 for maximum */
maxphase_aux(phase2, vol2, phasem, volm, &xphase2, &xvol2);
} else {
/* not possible? */
return;
}
if (xvol1 > volm) {
*volret = xvol1;
*phaseret = xphase1;
}
if (xvol2 > volm) {
*volret = xvol2;
*phaseret = xphase2;
}
}
void maxphase(int *phaseret, float *volret)
{
float v0 = probe(0);
float v360 = probe(360);
maxphase_aux(0, v0, 360, v360, phaseret, volret);
}
UPDATE: 2012-11-10.
#include <stdio.h>
#include <string.h>
#include <math.h>
#define FAKE_TARGET 89
unsigned fake_target = FAKE_TARGET;
float probe_one(unsigned int phase);
void Maxphase(float *max, unsigned int *index);
void Maxphase(float *max, unsigned int *index)
{
unsigned int aim, idx, victim;
struct best {
unsigned pos;
float val;
} samples[4] = {{0, 0.0}, };
for (aim = 0;aim < 360;aim += 90) {
idx=aim/90;
samples[idx].pos = aim;
samples[idx].val = probe_one(samples[idx].pos);
if (!idx || samples[idx].val < samples[victim].val ) victim = idx;
}
/* eliminate the weakist postion, and rotate the rest,
** such that:
** samples[0] := lower boundary.
** samples[1] := our best guess
** samples[2] := upper boundary
** samples[3] := scratch/probe element
*/
fprintf(stderr, "Victim=%u\n", victim );
switch(victim) {
case 0: samples[0] = samples[1]; samples[1] = samples[2]; samples[2] = samples[3]; break;
case 1: samples[1] = samples[3]; samples[3] = samples[0]; samples[0] = samples[2]; samples[2] = samples[3]; break;
case 2: samples[2] = samples[1]; samples[1] = samples[0]; samples[0] = samples[3]; break;
case 3: break;
}
/* Calculation is easier if the positions are increasing.
** (We can always perform the modulo 360 if needed)
*/
if (samples[0].pos > samples[1].pos ) samples[1].pos += 360;
if (samples[1].pos > samples[2].pos ) samples[2].pos += 360;
while( 1) {
int step;
step = samples[2].pos - samples[0].pos;
if (step < 3) break;
do {
fprintf(stderr, "\n[%u %u %u] Diff=%d\n"
, samples[0].pos , samples[1].pos , samples[2].pos , step);
if (step > 0) step++; else step--;
step /= 2;
aim = (samples[0].pos + step ) ;
/* avoid hitting the middle cell twice */
if (aim %360 != samples[1].pos %360) break;
step += 1;
aim = (samples[0].pos + step ) ;
if (aim %360 != samples[1].pos %360) break;
step -= 2;
aim = (samples[0].pos + step ) ;
break;
} while(0);
fprintf(stderr, "Step=%d Aim=%u, Idx=%u\n",step, aim,idx );
samples[3].pos = aim;
samples[3].val = probe_one( samples[3].pos );
victim= (samples[3].pos > samples[1].pos ) ? 2 : 0;
if (samples[3].val > samples[1].val) idx= 1; else idx = victim;
fprintf(stderr, "Victim=%u, TargetIdx=%u\n", victim, idx );
/* This should not happen */
if (samples[3].val < samples[victim].val) break;
if (idx != victim) samples[2-victim] = samples[idx];
samples[idx] = samples[3];
}
*max = samples[1].val;
*index = samples[1].pos % 360;
}
float probe_one(unsigned int phase)
{
float value;
#ifdef FAKE_TARGET
int dif;
dif = fake_target-phase;
if (dif < -180) dif = 360+dif;
else if (dif > 180) dif = 360-dif;
/* value = 1.0 / (1 + pow(phase-231, 2)); */
value = 1.0 / (1 + pow(dif, 2));
fprintf(stderr, "Target = %d: Probe(%d:%d) := %f\n", fake_target, phase, dif, value );
sleep (1);
#else
unsigned int data;
phase %= 360;
phaseset(phase);
delay_ms(100);
data = readvalue(); // what is this ?
value = voltage(mux1);
#endif
return value;
}
int main(int argc, char **argv)
{
float value;
unsigned int index;
if (argv[1]) sscanf (argv[1], "%u", &fake_target);
fake_target %= 360;
Maxphase(&value, &index) ;
printf("Phase=%u Max=%f\n", index, value );
return 0;
}

Resources