void evolve(board prv, board nxt){
int i, j;
int n;
printf("\rGeneration %d\n", generation++);
if (printLazy == 1){
lazyPrint(prv);
for (j=0; j < WIDTH; ++j) {
for (i = 0; i < HEIGHT; ++i) {
n = neighbors(prv, i, j);
if (prv[i][j] && (n == 3 || n == 2))
nxt[i][j] = true;
else if (!prv[i][j] && (n == 3))
nxt[i][j] = true;
else
nxt[i][j] = false;
}
}
}
** Some asked me to add the neighbors method so
static int neighbors (board b, int i, int j) {
int n = 0;
int i_left = max(0,i-1);
int i_right = min(HEIGHT, i+2);
int j_left = max(0,j-1);
int j_right = min(WIDTH, j+2);
int ii, jj;
for (ii = i_left; ii < i_right; ++ii) {
for (jj = j_left; jj < j_right; ++jj) {
n += b[ii][jj];
}
}
return n - b[i][j];
}
So I am working on optimizing this so that it will go faster and I'm stuck on how to optimize this more. Here's what I have so far
void evolve(board prv, board nxt) {
register int i, j;
int n;
bool next;
printf("\rGeneration %d\n", generation++);
if (printLazy == 1){
lazyPrint(prv);
}
for (j=0; j < WIDTH; ++j) {
for (i = 0; i < HEIGHT; ++i) {
n = neighbors(prv, i, j);
if (prv[i][j])
if (n == 2)
next = true;
else if (n == 3)
next = true;
else
next = false;
else
if(n == 3)
next = true;
else
next = false;
nxt[i][j] = next;
}
}
}
Is there a better way to do this or are there any resources or videos y'all recommend?
Thanks, any help is appreciated.
Some ideas Inline your function neighbors(). Or turn it into a macro. Tidy up the conditional. To unroll the inner loop replace every use of i with the literal values so your code looks like :
for (j =0;.......
n = fun(prev, 0 ,j);
If.....
n = fun(prev, 1, j);
if......
and so on.
If the value of HEIGHT was let's say 100, then you get a code explosion of 100 function calls and 100 compound conditionals. Even worse if you unroll the outer loop.
If n was limited to say 8 neighbors, use a lookup table
bool foo[2][8] = { [1][2] = true, [1][3] = true, [0][3] = true };
for (j=0; j < WIDTH; ++j) {
for (i = 0; i < HEIGHT; ++i) {
n = neighbors(prv, i, j);
nxt[i][j] = foo[prv[i][j]][n];
}
}
A common weakness is the neighbors(prv, i, j) function itself. One trick to to oversize the 2D array by 1 on all four sides and populate the edge with false so neighbors() can always check 8 neighbors as it is never used on the edge/corners.
Making sure the 2nd dimension is a power of 2 helps also - simplifies index calculation. So if the original array way 12*11, make the new array (1+12+1)*(1+11+1+4) or 14*16.
Related
The problem is to create an array of player ranks based on 2 other arrays: leaderboard and player scores. More explanations of the problem here: https://www.hackerrank.com/challenges/climbing-the-leaderboard/problem.
The code below is a spaghetti but it's working fine. But, for large size of ranked array(200000 elements for example), it times out. I'm not asking for code to copy/paste. I just wanna know if there is a way to optimize this code.
int* climbingLeaderboard(int ranked_count, int* ranked, int player_count, int* player, int* result_count) {
*result_count=player_count;
// remove duplicates
int removed=0;
for(int i=0, j=1; i<ranked_count-removed; i++, j++){
if(ranked[i]==ranked[j]){
for(int k=j; k<ranked_count-removed; k++)
ranked[k]=ranked[k+1];
removed++;
}
}
int newsize=ranked_count-removed;
// create an array to store ranks then fill it
int* positions=malloc(newsize*sizeof(int));
positions[0]=1;
for(int i=0, j=1; j<newsize; i++, j++){
positions[j]=(ranked[j]<ranked[i])? (positions[i]+1) : positions[i];
}
// create and fill the results array using binary search
int* res = malloc(player_count*sizeof(int));
int start=0, end=newsize-1, middle=(start+end)/2;
int j, k=newsize-1;
for(int i=0; i<player_count; i++){
if(i>0&&player[i]==player[i-1]){
*(res+i)=(*(res+(i-1)));
continue;
}
if(player[i]>=ranked[middle]){
*(res+i)=positions[middle];
j=middle-1;
while(j>=0){
if(player[i]>=ranked[j])
*(res+i)=positions[j];
else if(j==k)
*(res+i)=positions[j]+1;
else break;
--j;
}
start=0; end=middle-1;
}
else{
*(res+i)=positions[newsize-1]+1;
j=newsize-1;
while(j>=middle){
if(player[i]>=ranked[j])
*(res+i)=positions[j];
else if(j==k)
*(res+i)=positions[j]+1;
else break;
--j;
}
start=middle+1; end=newsize-1;
}
middle=(start+end)/2;
}
free(positions);
return res;
}
The initial loop to remove duplicates has a potential quadratic time complexity. You can achieve linear complexity using the 2 finger approach:
int removed = 0;
for (int i = 1, j = 1; j < ranked_count; j++) {
if (ranked[i - 1] != ranked[j])
ranked[i++] = ranked[j];
else
removed++;
}
More generally, the argument arrays should not be changed in spite of the sloppy prototype given:
int *climbingLeaderboard(int ranked_count, int *ranked,
int player_count, int *player,
int *result_count);
Here are simple steps I would recommend to solve this problem:
allocate and initialize a ranking array with the ranking for each of the scores in the ranked array. Be careful to allocate ranked_count + 1 elements.
allocate a result array res of length player_count, set the result_count to player_count.
starting with pos = ranked_count, for each entry i in player:
locate the position pos where the entry would be inserted in the ranking array using binary search between position 0 and the current pos inclusive. Make sure you find the smallest entry in case of duplicate scores.
set res[i] to ranking[pos]
free the ranking array
return the res array.
Here is a simple implementation:
int *climbingLeaderboard(int ranked_count, int *ranked,
int player_count, int *player,
int *result_count)
{
if (player_count <= 0) {
*result_count = 0;
return NULL;
}
int *ranking = malloc(sizeof(*ranking) * (ranked_count + 1));
int rank = 1;
ranking[0] = rank;
for (int i = 1; i < ranked_count; i++) {
if (ranked[i] != ranked[i - 1])
rank++;
ranking[i] = rank;
}
ranking[ranked_count] = rank + 1;
int *res = malloc(sizeof(*res) * player_count);
*result_count = player_count;
int pos = ranked_count;
for (int i = 0; i < player_count; i++) {
int start = 0;
while (start < pos) {
int middle = start + (pos - start) / 2;
if (ranked[middle] > player[i])
start = middle + 1;
else
pos = middle;
}
res[i] = ranking[pos];
}
free(ranking);
return res;
}
Look for ways to use "branchless" to improve execution speed:
positions[0]=1;
for(int i=0, j=1; j<newsize; i++, j++){
positions[j]=(ranked[j]<ranked[i])? (positions[i]+1) : positions[i];
}
becomes
positions[0] = 1;
for( int i = 0, j = 1; j < newsize; i++, j++ )
positions[j] = positions[i] + (ranked[j] < ranked[i]);
Other than this, I don't even want to try to sort out what this code is attempting.
I'm trying to solve the 8 queens puzzle problem in C. I'm having problems with the recursive search. The program is supposed to start at a given column:
execute(tabuleiro,8,0);
Where the 8 is the number of columns in the board, and 0 is the start column.
This works when I start at column 0. When I send any other column number to the recursive search, the program just counts to the last column. For example, if I choose to start the search from the number 5 column, the code search from the column 5 to 7, after this it should search from 0 to 4, but it doesn't do that.
If I do this:
execute(tabuleiro,8,3);
It fills in only the last 5 colummns, and does not return to column 0 to finish the solution:
Also, how can I select the initial position for the queen in this code? Like I said before, the column is assigned in the code, but I'm not sure how to pick the correct column.
The code has 3 functions: one is to display the board, a second to check if the move is legal (so one queen doesn't attack the other), and the last one to place one queen and recur for the remainder of the board.
#include <stdlib.h>
#include <windows.h>
int sol = 0;
void viewtab(int tab[][8], int N)
{
int i,j;
for( i = 0; i < N; i++)
{
for( j = 0; j < N; j++)
{
if(tab[i][j] == 1)
printf("R\t");
else
printf("-\t");
}
printf("\n\n");
}
printf("\n\n");
system("pause");
printf("\n");
}
int secury(int tab[][8], int N, int lin, int col)
{
// this function is to check if the move is secury
int i, j;
// attack in line
for(i = 0; i < N; i++)
{
if(tab[lin][i] == 1)
return 0;
}
//attack in colune
for(i = 0; i < N; i++)
{
if(tab[i][col] == 1)
return 0;
}
// attack in main diagonal
//
for(i = lin, j = col; i >= 0 && j >= 0; i--, j--)
{
if(tab[i][j] == 1)
return 0;
}
for(i = lin, j = col; i < N && j < N; i++, j++)
{
if(tab[i][j] == 1)
return 0;
}
// attack in main secondary
for(i = lin, j = col; i >= 0 && j < N; i--, j++)
{
if(tab[i][j] == 1)
return 0;
}
for(i = lin, j = col; i < N && j >= 0; i++, j--)
{
if(tab[i][j] == 1)
return 0;
}
// if arrive here the move is secury and return true
return 1;
}
void execute(int tab[][8], int N, int col)
{
int i;
if(col == N)
{
printf("Solution %d ::\n\n", sol + 1);
viewtab(tab, N);
sol++;
return;
}
for( i = 0; i < N; i++)
{
// check if is secury to put the queen at that colune
if(secury(tab, N, i, col))
{
// insert the queen (with 1)
tab[i][col] = 1;
// call recursive
execute(tab, N, col + 1);
// remove queen (backtracking)
tab[i][col] = 0;
}
}
}
int main()
{
int i, j, tabuleiro[8][8];
for (i = 0; i < 8; i = i + 1)
for (j = 0; j < 8; j = j + 1) tabuleiro[i][j] = 0;
execute(tabuleiro,8,0);
return 0;
}
The search always stops in the rightmost column because you specifically tell it to stop there:
void execute(int tab[][8], int N, int col)
{
int i;
if(col == N)
{
printf("Solution %d ::\n\n", sol + 1);
viewtab(tab, N);
sol++;
return;
}
Look at your termination condition: you check the current column against the highest column number, and stop there.
If you want to go back to column 0, you have to change your loop logic. For instance, let col reach N, at which point you reset it to 0, and let it continue until you hit the original value. Another way is to continue until the count of placed queens is N.
You choose the initial point in the same way: you pick the first one and make your recursive call. If that eventually results in a solution, you print it. If not, your top-most call continues to the next row (line) of the board and puts the first queen there.
This is already in your main logic. Just make sure that secury will return true when the board is empty, rather than false or throwing an error.
A. You can place the first Queen at (0,0).
B. And begin the search also from (0,0).
C. I do not see any need to start looking for some other index.
Successfully!!
#include <stdio.h>
#include <math.h>
int n=4;
int GetQueenSettings(int board[4][4],int currentRow,int n)
{
//decide when the recursion stops
if(currentRow==n)
return 1; //successful setting
//otherwise we set column by column in this row and continue
int TotalSettingCount=0;
for(int i=0;i<n;i++)
{
//make sure it can be set (it is unset at that moment)
if(board[currentRow][i]==0)
{
board[currentRow][i]==1+currentRow;
//use row related info for settings
//now set invalid positions for remaining rows
setInvalid(board,currentRow,n,i);
//recover after this before trying the next
TotalSettingCount += GetQueenSettings(board,currentRow+1,n);
board[currentRow][i]=0;
RecoverBoard(board,currentRow,n);
}
}
return TotalSettingCount;
}
void setInvalid(int board[4][4],int currentRow,int n,int i)
{
//vertical and diagonal elements
for(int row=currentRow+1;row<n;row++) //start from the next line
{
//firstly make sure board can be set
if(board[row][i]==0)//vertical position
board[row][i]=-(1+currentRow);
//now check diagonal
int rowGap=row-currentRow;
if(i-rowGap>=0 && board[row][i-rowGap]==0)
{
//left bottom diagonal position
board[row][i-rowGap]=-(1+currentRow);
}
if(i+rowGap<n && board[row][i+rowGap]==0)
{
//bottom right diagonal position
board[row][i+rowGap]=-(1+currentRow);
}
}
}
void RecoverBoard(int board[4][4],int currentRow,int n)
{
//recover is to check all remaining rows if index is higher than current row(setters)
//OR less than -currentRow(invalids)!
for(int row=currentRow+1;row<n;row++)
{
for(int col=0;col<n;col++)
{
if(board[row][col]>currentRow || board[row][col]< -currentRow)
board[row][col]=0;
}
}
}
int main()
{
int board[n][n];
printf("Number of settings:-> %d",GetQueenSettings(board,0,n));
return 0;
}
There are N queeens placed on a NxN chessboard without interfering with each other. when i run this code i get the answer as zero instead of 2 . also i cant figure out a way of passing array board to functions with variable size(size will be given by user).What am i doing wrong?!
You should initialize your board. As is, you start with a board full of garbage values. You are using a variable-length array as board. Such arrays cannot be initialized, so you have to set the board to all zero with a loop or with memset from <string.h>:
int board[n][n];
memset(board, 0, sizeof(board));
You can pass variable-length arrays to functions when the dimensions are passed in as earlier arguments, for example:
int GetQueenSettings(int n, int board[n][n], int currentRow) { ... }
Also fix the =/== switch in setInvalid:
if (board[row][i] == 0)
board[row][i] = -(1 + currentRow);
Finally make sure that all functions have proper prototypes when they are called.
A loooooooong time ago I developed an algorithm like the one you have, maybe it can help you.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
long Solutions;
void AllocBoard(int *** Board, int Queens)
{
int i;
*Board = (int **)malloc(sizeof(int *) * Queens);
for(i = 0; i < Queens; i++)
{
(*Board)[i] = (int *)malloc(sizeof(int) * Queens);
memset((*Board)[i], 0, sizeof(int) * Queens);
}
}
void DeallocBoard(int *** Board, int Queens)
{
int i;
for(i = 0; i < Queens; i++)
free((*Board)[i]);
free(*Board);
}
void SavePosition(int *** Board, int Queens, int Col, int Row, int Inc)
{
int i, j;
for(i = 0; i < Queens; i++)
{
if((*Board)[Col][i] >= 0) (*Board)[Col][i] += Inc;
if((*Board)[i][Row] >= 0) (*Board)[i][Row] += Inc;
}
for(i = Col, j = Row; j < Queens && i < Queens; i++, j++)
if((*Board)[i][j] >= 0) (*Board)[i][j] += Inc;
for(i = Col, j = Row; j >= 0 && i >= Col; i--, j--)
if((*Board)[i][j] >= 0) (*Board)[Col][j] += Inc;
for(i = Col, j = Row; j >= 0 && i < Queens; i++, j--)
if((*Board)[i][j] >= 0) (*Board)[i][j] += Inc;
for(i = Col, j = Row; j < Queens && i >= Col; i--, j++)
if((*Board)[i][j] >= 0) (*Board)[i][j] += Inc;
}
void FindSolutions(int *** Board, int Queens, int Col)
{
int i, j;
for(i = 0; i < Queens; i++)
{
if((*Board)[Col][i] != 0) continue;
(*Board)[Col][i] = -1;
if(Col + 1 == Queens)
Solutions++;
else
{
SavePosition(Board, Queens, Col, i, 1);
FindSolutions(Board, Queens, Col + 1);
SavePosition(Board, Queens, Col, i, -1);
}
(*Board)[Col][i] = 0;
}
}
void main(int argc, char **argv)
{
int Queens, ** Board = NULL;
clock_t Start, End;
clrscr();
if(argc < 2)
Queens = 8;
else
Queens = atoi(argv[1]);
Solutions = 0;
Start = clock();
AllocBoard(&Board, Queens);
FindSolutions(&Board, Queens, 0);
DeallocBoard(&Board, Queens);
End = clock();
printf("Solutions %ld\n", Solutions);
printf("Estimated time: %f", (End - Start) / CLK_TCK);
getch();
}
Hope this helps
Asking "why it doesn't work" about short and simple programs is not very well. Try debug it yourself with any debugger or simply using printing in important places/on each step. Actually, C has no arrays, instead it has pointers. You can work with them very alike, that is why all you need is to change int board[4][4] to int **board and add one argument: int N(count of cells) and that's all. All your other code shouldn't be changed.
I'm doing another exercise and I have to :
"Write a recursive function to print all solution to the eight queens chess problem, return the number of solutions and the function prototype must be : int function(void)
To work around the no argument rule I used static variables.
I've done it (with google's help), and it works, but they don't allow to use for loops and for some reason I can't manage to convert the last two for loops to while loops.
It' driving me crazy, it should be easy! I think it's the recursion that mess it up...
Here's the working function :
int function()
{
static int count = 0;
static int col = 0;
const int n = 8;
static int hist[8] = {10, 10, 10, 10, 10, 10, 10, 10};
int i1 = 0;
if (col == n) {
count++;
while (i1++ < n)
{
putchar('0' + hist[i1-1] + 1);
}
putchar('\n');
}
for (int i = 0; i < n; i++) {
int j = 0;
for (j = 0; j < col && !(hist[j] == i || (hist[j] - i) == col - j || -(hist[j] - i) == col - j); j++);
if (j < col) {
continue;
}
hist[col] = i;
col++;
function();
col--;
}
return count;
}
And I tried to convert the two last for loops to while loops like this :
int i = 0;
while (i < n)
{
int j = 0;
while (j < col && !(hist[j] == i || (hist[j] - i) == col - j || -(hist[j] - i) == col - j))
{
j++;
}
if (j < col) {
continue;
}
hist[col] = i;
col++;
function();
col--;
i++;
}
But it doesn't work, is there more the for loops than it seems ? I'm new to recursion, I thought I got it but it seems I was wrong...
I ran the code and found the problem. It is with the line
if (j < col) {
continue;
}
since this isn't a continue statement that goes to a for loop, you have to increment i in this condition as well.
if (j < col) {
i++; // add this line
continue;
}
You can change the first loop to
while(i++<n)
and it works fine.
I' having a problem allocating a structure in a function. Here is the code(I'm currently using visual studio 2008):
Mat3x3* ProdMat(Mat3x3 *m, Mat3x3 *n)
{
if(m == NULL || n == NULL)
{
cout << "\t[W] Cannot compute product of the two matrixes one or both are NULL." << endl;
return NULL;
}
Mat3x3 *p; // product
int i, j;
float sum = 0;
p = (Mat3x3*)malloc(sizeof(Mat3x3)); // <= Exp cannot be evaluated
for(i = 0; i < 3; i++)
{
for(j = 0; j < 3; j++)
{
sum = 0;
for(int k = 0; k < 3; k++)
{
float a = m->a[i][k];
float b = n->a[k][j];
sum += a * b;
}
p->a[i][j] = sum;
}
}
return p;
}
P contains a matrix with 9 entries. Here is the context in which the error is given:
Mat3x3* compute_final_trans(Trans **transes) // compute product of all transformation matrixes from right to left
{
int k_trans = 0, i, j;
Mat3x3 *final_trans;
if(transes == NULL)
{
printf("\t[E] Cannot compute sequence of NULL transformations.\n");
return NULL;
}
final_trans = (Mat3x3*)malloc(sizeof(final_trans));
for(i = 0; i < 3; i++) // generate eye matrix
for(j = 0; j < 3; j++)
{
if(i == j)
{
final_trans->a[i][j] = 1;
}
else
{
final_trans->a[i][j] = 0;
}
}
while(transes[k_trans++]);
for(i = k_trans - 2; i >= 0; i--)
{
final_trans = ProdMat(transes[i]->matrix, final_trans); // <= ERROR
}
return final_trans;
}
Final trans is initialised as the eye matrix and transes have been succesfully computed before this step(before calling compute_final_trans). The while is used to retreieve the number of transformations that transes contains. At line:
final_trans = ProdMat(transes[i]->matrix, final_trans);
ProdMat fails to allocate memory for p which is a pointer to a Mat3x3 structure.
perror suggests that there isn't enough memory to allocate to the structure. However I'm only using 1GB of RAM(4GB in all).
Any help/suggestion/reference will be very much appreciated.
Sebi
malloc(sizeof(final_trans))
This is bad. You are only allocating enough space for a pointer, not space for an array.