Optimizing a large if-else branch with binary search - c

So there is an if-else branch in my program with about 30 if-else statements. This part runs more than 100 times per second, so I saw it as an opportunity to optimize, and made it do binary search with a function pointer array (practically a balanced tree map) instead of doing linear if-else condition checks. But it ran slower about 70% of the previous speed.
I made a simple benchmark program to test the issue and it also gave similar result that the if-else part runs faster, both with and without compiler optimizations.
I also counted the number of comparisons done, and as expected the one doing binary search did about half number of comparisons than the simple if-else branch. But still it ran 20~30% slower.
I want to know where all my computing time is being wasted, and why the linear if-else runs faster than the logarithmic binary search?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
long long ifElseCount = 0;
long long binaryCount = 0;
int ifElseSearch(int i) {
++ifElseCount;
if (i == 0) {
return 0;
}
++ifElseCount;
if (i == 1) {
return 1;
}
++ifElseCount;
if (i == 2) {
return 2;
}
++ifElseCount;
if (i == 3) {
return 3;
}
++ifElseCount;
if (i == 4) {
return 4;
}
++ifElseCount;
if (i == 5) {
return 5;
}
++ifElseCount;
if (i == 6) {
return 6;
}
++ifElseCount;
if (i == 7) {
return 7;
}
++ifElseCount;
if (i == 8) {
return 8;
}
++ifElseCount;
if (i == 9) {
return 9;
}
}
int getZero(void) {
return 0;
}
int getOne(void) {
return 1;
}
int getTwo(void) {
return 2;
}
int getThree(void) {
return 3;
}
int getFour(void) {
return 4;
}
int getFive(void) {
return 5;
}
int getSix(void) {
return 6;
}
int getSeven(void) {
return 7;
}
int getEight(void) {
return 8;
}
int getNine(void) {
return 9;
}
struct pair {
int n;
int (*getN)(void);
};
struct pair zeroToNine[10] = {
{0, getZero},
{2, getTwo},
{4, getFour},
{6, getSix},
{8, getEight},
{9, getNine},
{7, getSeven},
{5, getFive},
{3, getThree},
{1, getOne},
};
int sortCompare(const void *p, const void *p2) {
if (((struct pair *)p)->n < ((struct pair *)p2)->n) {
return -1;
}
if (((struct pair *)p)->n > ((struct pair *)p2)->n) {
return 1;
}
return 0;
}
int searchCompare(const void *pKey, const void *pElem) {
++binaryCount;
if (*(int *)pKey < ((struct pair *)pElem)->n) {
return -1;
}
if (*(int *)pKey > ((struct pair *)pElem)->n) {
return 1;
}
return 0;
}
int binarySearch(int key) {
return ((struct pair *)bsearch(&key, zeroToNine, 10, sizeof(struct pair), searchCompare))->getN();
}
struct timer {
clock_t start;
clock_t end;
};
void startTimer(struct timer *timer) {
timer->start = clock();
}
void endTimer(struct timer *timer) {
timer->end = clock();
}
double getSecondsPassed(struct timer *timer) {
return (timer->end - timer->start) / (double)CLOCKS_PER_SEC;
}
int main(void) {
#define nTests 500000000
struct timer timer;
int i;
srand((unsigned)time(NULL));
printf("%d\n\n", rand());
for (i = 0; i < 10; ++i) {
printf("%d ", zeroToNine[i].n);
}
printf("\n");
qsort(zeroToNine, 10, sizeof(struct pair), sortCompare);
for (i = 0; i < 10; ++i) {
printf("%d ", zeroToNine[i].n);
}
printf("\n\n");
startTimer(&timer);
for (i = 0; i < nTests; ++i) {
ifElseSearch(rand() % 10);
}
endTimer(&timer);
printf("%f\n", getSecondsPassed(&timer));
startTimer(&timer);
for (i = 0; i < nTests; ++i) {
binarySearch(rand() % 10);
}
endTimer(&timer);
printf("%f\n", getSecondsPassed(&timer));
printf("\n%lli %lli\n", ifElseCount, binaryCount);
return EXIT_SUCCESS;
}
possible output:
78985494
0 2 4 6 8 9 7 5 3 1
0 1 2 3 4 5 6 7 8 9
12.218656
16.496393
2750030239 1449975849

You should look at the generated instructions to see (gcc -S source.c), but generally it comes down to these three:
1) N is too small.
If you only have a 8 different branches, you execute an average of 4 checks (assuming equally probable cases, otherwise it could be even faster).
If you make it a binary search, that is log(8) == 3 checks, but these checks are much more complex, resulting in an overall more code executed.
So, unless your N is in the hundreds, it probably doesn't make sense to do this. You could do some profiling to find the actual value for N.
2) Branch prediction is harder.
In case of a linear search, every condition is true in 1/N cases, meaning the compiler and branch predictor can assume no branching, and then recover only once. For a binary search, you likely end up flushing the pipeline once every layer. And for N < 1024, 1/log(N) chance of misprediction actually hurts the performance.
3) Pointers to functions are slow
When executing a pointer to a function you have to get it from memory, then you have to load your function into instruction cache, then execute the call instruction, the function setup and return. You can not inline functions called through a pointer, so that is several extra instructions, plus memory access, plus moving things in/out of the cache. It adds up pretty quickly.
All in all, this only makes sense for a large N, and you should always profile before applying these optimizations.

Use a switch statement.
Compilers are clever. They will produce the most efficient code for your particular values. They will even do a binary search (with inline code) if that is deemed more efficient.
And as a huge benefit, the code is readable, and doesn't require you to make changes in half a dozen places to add a new case.
PS. Obviously your code is a good learning experience. Now you've learned, so don't do it again :-)

Related

Linear Recursion Vectorization

To vectorize the following mathematical expression (linear recursion):
f(i)=f(i-1)/c+g(i), i starts at 1, f(0), c are constant numbers and given.
I can get better speed by using list-comprehension, that is:
def function():
txt= [0,2,0,2,0,2,0,2,2,2,0,2,2,2,0,2,0,2,2,2,0,2,0,2,0,2,0,2,2,2,0,2,0,2,0,2,0,2,2,2]
indices_0=[]
vl=0
sb_l=10
CONST=512
[vl := vl+pow(txt[i],(i+1)) for i in range(sb_l)]
if (vl==1876):
indices_0=[0]
p=[i for i in range(1,len(txt)-sb_l+1) if (vl := (vl-txt[i-1])/2+ txt[i+sb_l-1]*CONST)==1876]
print(indices_0+p)
function()
I am looking for a vectorized/faster than vectorization (if possible!) implementation of the above code in python/c.
Note:
1.
A linear recursive function is a function that only makes a single call to itself each time the function runs (as opposed to one that would call itself multiple times during its execution). The factorial function is a good example of linear recursion.
2.
Note that all variables array are given for demonstration purpose, the main part for vectorization is :
[vl := vl+pow(txt[i],(i+1)) for i in range(sb_l)]
if (vl==1876):
indices_0=[0]
p=[i for i in range(1,len(txt)-sb_l+1)
if (vl := (vl-txt[i-1])/2+ txt[i+sb_l-1]*CONST)==1876]
Here, f(i-1)= (vl-txt[i-1]), c=2, g(i)= txt[i+sb_l-1]*CONST.
POSTSCRIPT: I am currently doing it in python, would it be much faster if it is implemented in C language's vectorization?
Here is an example of equivalent C program doing the same thing but faster:
#include <stdio.h>
#include <stdlib.h>
// See: https://stackoverflow.com/questions/29787310/does-pow-work-for-int-data-type-in-c
int64_t int_pow(int64_t base, int exp)
{
int64_t result = 1;
while (exp)
{
// Branchless optimization: result *= base * (exp % 2);
if(exp % 2)
result *= base;
exp /= 2;
base *= base;
}
return result;
}
void function()
{
// Both txt values and sb_l not be too big or it will cause silent overflows (ie. wrong results)
const int64_t txt[] = {0,2,0,2,0,2,0,2,2,2,0,2,2,2,0,2,0,2,2,2,0,2,0,2,0,2,0,2,2,2,0,2,0,2,0,2,0,2,2,2};
const size_t txtSize = sizeof(txt) / sizeof(txt[0]);
const int sb_l = 10;
const int64_t CONST = 512;
int64_t vl = 0;
int64_t* results = (int64_t*)malloc(txtSize * sizeof(int64_t));
size_t cur = 0;
// Optimization so not to compute pow(0,i+1) which is 0
for (int i = 0; i < sb_l; ++i)
if(txt[i] != 0)
vl += int_pow(txt[i], i+1);
if (vl == 1876)
{
results[cur] = 0;
cur++;
}
for (int i = 1; i < txtSize-sb_l+1; ++i)
{
vl = (vl - txt[i-1]) / 2 + txt[i+sb_l-1] * CONST;
if(vl == 1876)
results[cur++] = i;
}
// Printing
printf("[");
for (int i = 0; i < cur; ++i)
{
if(i > 0)
printf(", ");
printf("%ld", results[i]);
}
printf("]\n");
fflush(stdout);
free(results);
}
int main(int argc, char* argv[])
{
function();
return 0;
}
Be careful with overflows. You can put assertions if you are unsure about that in specific places (note they make the code slower when enabled though). Please do not forget to compile the program with optimizations (eg. -O3 with GCC and Clang and /O2 with MSVC).

How to find the minimum number of coins needed for a given target amount(different from existing ones)

This is a classic question, where a list of coin amounts are given in coins[], len = length of coins[] array, and we try to find minimum amount of coins needed to get the target.
The coins array is sorted in ascending order
NOTE: I am trying to optimize the efficiency. Obviously I can run a for loop through the coins array and add the target%coins[i] together, but this will be erroneous when I have for example coins[] = {1,3,4} and target = 6, the for loop method would give 3, which is 1,1,4, but the optimal solution is 2, which is 3,3.
I haven't learned matrices and multi-dimensional array yet, are there ways to do this problem without them? I wrote a function, but it seems to be running in an infinity loop.
int find_min(const int coins[], int len, int target) {
int i;
int min = target;
int curr;
for (i = 0; i < len; i++) {
if (target == 0) {
return 0;
}
if (coins[i] <= target) {
curr = 1 + find_min(coins, len, target - coins[i]);
if (curr < min) {
min = curr;
}
}
}
return min;
}
I can suggest you this reading,
https://www.geeksforgeeks.org/generate-a-combination-of-minimum-coins-that-results-to-a-given-value/
the only thing is that there is no C version of the code, but if really need it you can do the porting by yourself.
Since no one gives a good answer, and that I figured it out myself. I might as well post an answer.
I add an array called lp, which is initialized in main,
int lp[4096];
int i;
for (i = 0; i <= COINS_MAX_TARGET; i++) {
lp[i] = -1;
}
every index of lp is equal to -1.
int find_min(int tar, const int coins[], int len, int lp[])
{
// Base case
if (tar == 0) {
lp[0] = 0;
return 0;
}
if (lp[tar] != -1) {
return lp[tar];
}
// Initialize result
int result = COINS_MAX_TARGET;
// Try every coin that is smaller than tar
for (int i = 0; i < len; i++) {
if (coins[i] <= tar) {
int x = find_min(tar - coins[i], coins, len, lp);
if (x != COINS_MAX_TARGET)
result = ((result > (1 + x)) ? (1+x) : result);
}
}
lp[tar] = result;
return result;
}

Suggestions/improvements on my simple Blackjack program

I am trying to get a head start on my class next semester so I made this basic version of Blackjack to start understanding the basics of C and I would love any thoughts you have that could help me gain a better understanding of C and its normal coding practices.
A lot of the things in C are new to me as I am coming from a background in JAVA so if I made a mistake in function declaration, in my use of pointers, or if I was thinking about how to approach the problem incorrectly and should have done things a completely different way please let me know.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
const int handSize = 2;
int randCard(int *isAce);
int sumCards(int cards[], int *hasAce[2]);
int main() {
srand(time(NULL));
int playGame = 0;
int dealerIsAce[handSize];
int *dealerAcePointers[handSize];
int playerIsAce[handSize];
int *playerAcePointers[handSize];
for (int i = 0; i < handSize; i++) {
dealerIsAce[i] = 0;
playerIsAce[i] = 0;
dealerAcePointers[i] = &dealerIsAce[0];
playerAcePointers[i] = &playerIsAce[0];
}
int dealerCards[] = {randCard(dealerAcePointers[0]), randCard(dealerAcePointers[1])};
int playerCards[] = {randCard(playerAcePointers[0]), randCard(playerAcePointers[1])};
int dealerSum;
int playerSum;
do {
printf("The dealer:\n? + %d\n\n", dealerCards[1]);
dealerSum = sumCards(dealerCards, dealerAcePointers);
if (dealerSum > 17) {
dealerCards[0] = dealerSum;
dealerCards[1] = randCard(dealerAcePointers[1]);
}
playerSum = sumCards(playerCards, playerAcePointers);
printf("You:\n%d + %d = %d", playerCards[0], playerCards[1], playerSum);
if (playerSum > 21) {
printf(" BUSTED");
playGame = 1;
} else {
printf("\nWould you like to \"hit\" or \"stand\"?\n");
}
if (playGame == 0) {
char stream[10];
if (strcmp(gets(stream), "hit") == 0) {
playerCards[0] = playerSum;
playerCards[1] = randCard(playerAcePointers[1]);
} else {
playGame = 1;
}
}
} while (playGame == 0);
if (playerSum > 21) {
if (dealerSum > 21) {
printf("\nTie!");
} else {
printf("\nDealer Wins!");
}
} else {
if (playerSum > dealerSum) {
printf("\nPlayer Wins!");
} else if (playerSum == dealerSum) {
printf("\nTie!");
} else if (playerSum < dealerSum) {
printf("\nDealer Wins!");
}
}
return 0;
}
int randCard(int *isAce) {
int card = rand() % 13 + 2;
if (card > 11) {
card = 10;
} else if (card == 11) {
*isAce = 1;
}
return card;
}
int sumCards(int cards[], int *hasAce[2]) {
int sum = cards[0] + cards[1];
if (sum > 21 && *hasAce[0] == 1) {
sum -= 10;
*hasAce[0] = *hasAce[1];
if (*hasAce[1] == 1) {
*hasAce = 0;
}
}
return sum;
}
As mentioned by a commenter, this could be better asked elsewhere, however I'm going to offer some opinions anyway. These are all opinions, and everyone will probably disagree with something I've said.
Incidentally, I'm entirely ignoring the rules of BlackJack and assuming that all your logic is correct.
First and foremost, there aren't any comments in the code. You mention this being for a class, therefore commenting is even more important as some poor person has to decipher a load of these to work out what they do. (Commenting code is important anyway incidentally, I always use the "Will I work out what this does in a months time" approach)
Having that much stuff in main() is unusual. I would personally break it out into a different function. You could then also consider putting it in a separate file, with a header file for the function declarations.
handSize is being used as a constant, you could probably make this a preprocessor macro instead: #define HAND_SIZE 2
The do-while loop could be replaced with a while(true) loop, then using the 'break' keyword to escape when you're done (Where you are currently setting playGame = 1. This also has the advantage of not having the if(playGame == 0) conditional. Also, in C, a boolean variable is 1 for true and 0 for false, so it would be more normal to have int playGame = 1; and then do { } while(playGame) and playGame = 0; when you're done with the loop. This case is a special in that you actually want to break out, rather than run to the end of the loop.
gets() was removed in C11 for security reasons (Implicit declaration of 'gets')
On a more whole-program points. These are even more subjective, and are mostly just how I would have solved the problem:
I personally would make dealerCards and playerCards large enough to hold the maximum possible number of cards (which I think is 5 in blackjack?) and initialise them to 0. Currently you are assigning the sum of the current cards to the first element of the dealerCards array, meaning that the values are not actual cards.
Rather than use separate arrays to track whether or not cards are aces, I would have made an enum for {EMPTY_SLOT, ACE, TWO, ..., JACK, QUEEN, KING} and then stored that in my Cards arrays. randCard can then just return a member of the enum, and take no arguments, and sumCards just iterates across the array and sums it. This also means that you can display the user's actual hand to them, rather than just the total.
For reference purposes, I've modified your code to how I would do it. The logic may not be perfect (or the exact same version of blackjack) but this is the sort of thing I would submit for a "program blackjack in C" homework. N.B. This could also do with a few more comments, particularly a block one at the top explaining what the general structure is.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define HAND_SIZE 5
typedef enum
{
// Technically I didn't need to set the values, as they are the defaults but
// it's good to be explicit when you're using the order for something.
EMPTY = 0,
ACE = 1,
TWO = 2,
THREE,
FOUR,
FIVE,
SIX,
SEVEN,
EIGHT,
NINE,
TEN,
JACK,
QUEEN,
KING
} card_t; // Types you typedef generally end _t as a convention.
// These should be in a separate header, but I'm keeping this in 1 file for StackOverflow
card_t randCard();
int sumCards(card_t cards[]);
void play();
int main()
{
srand(time(NULL));
play();
return 0;
}
card_t randCard()
{
int card = rand() % 13 + 1;
return (card_t)card;
}
int sumCards(card_t cards[])
{
int total = 0;
int num_aces = 0;
for (int i = 0; i < HAND_SIZE; i++) {
switch(cards[i]) {
case ACE:
num_aces++;
total += 11;
break;
case JACK:
case QUEEN:
case KING:
total += 10;
break;
default:
total += (int)cards[i]; // Relying here on the fact that the cards are in the correct order.
break;
}
}
while (num_aces > 0 && total > 10) {
total -= 10;
num_aces--;
}
return total;
}
void play()
{
card_t playerCards[HAND_SIZE];
card_t dealerCards[HAND_SIZE];
card_t dealerKnown[HAND_SIZE]; // Equivalent to dealer cards, but with first 2 elements blank
for (int i = 0; i < HAND_SIZE; i++) {
playerCards[i] = EMPTY;
dealerCards[i] = EMPTY;
dealerKnown[i] = EMPTY;
}
playerCards[0] = randCard();
playerCards[1] = randCard();
dealerCards[0] = randCard();
dealerCards[1] = randCard();
int num_cards = 2;
while(num_cards <= HAND_SIZE) {
printf("The dealer: ? + %d\n\n", sumCards(dealerKnown));
if (sumCards(dealerCards) > 17) {
dealerCards[num_cards] = randCard();
}
int playerSum = sumCards(playerCards);
printf("Your total: %d\n", playerSum);
if (playerSum > 21) {
printf("BUSTED\n");
break;
} else {
printf("Would you like to \"hit\" or \"stand\"?\n");
}
char stream[10];
if (strcmp(fgets(stream, sizeof(stream), stdin), "hit\n") != 0) {
break;
}
playerCards[num_cards] = randCard();
num_cards++;
}
printf("\n"); // Printing the new line separately rather than at the beginning of all the strings below
int playerSum = sumCards(playerCards);
int dealerSum = sumCards(dealerCards);
if (playerSum > 21) {
if (dealerSum > 21) {
printf("Tie!");
} else {
printf("Dealer Wins!");
}
} else {
if (playerSum > dealerSum) {
printf("Player Wins!");
} else if (playerSum == dealerSum) {
printf("Tie!");
} else if (playerSum < dealerSum) {
printf("Dealer Wins!");
}
}
printf("\n");
}

How can I resolve this recursive overflow?

#define MIN -2147483648
long max(long x,long y)
{
long m=x;
if(y>x)
m=y;
return m;
}
long f(int x,int y,int **p)
{
long result;
if(x<0||y<0)
result = MIN;
else
if(x==0&&y==0)
result = p[0][0];
else
result = max(f(x-1,y,p),f(x,y-1,p))+p[x][y];
return result;
}
int main(void)
{
int n;
scanf("%d",&n);
int** p = (int **)malloc(n*sizeof(int*));
for(int i=0;i<n;i++)
{
p[i] = (int*)malloc(n*sizeof(int));
for(int j=0;j<n;j++)
scanf("%d",p[i]+j);
}
printf("haha\n");
printf("%ld\n",f(n-1,n-1,p));
return 0;
}
when I assign 10 to n, it works well.But when I assign 20 to n, there's no result put out. I googled it and I guessed that the error may be a recursive overflow. So how can I resolve this problem?
You're making a very large number of recursive calls. At each level, you make twice the number of calls as the prior level. So when N is 20, you're making 2^20 = 1048576 function calls. That takes a long time.
Most of these calls keep recomputing the same values over and over again. Rather that recomputing these values, calculate them only once.
Here's a non-recursive method of doing this:
long f(int x,int y,int **p)
{
long **p2;
int i, j;
p2 = malloc(sizeof(long *)*(x+1));
for (i=0;i<=x;i++) {
p2[i] = malloc(sizeof(long)*(y+1));
for (j=0;j<=y;j++) {
if (i==0 && j==0) {
p2[i][j] = p[i][j];
} else if (i==0) {
p2[i][j] = p2[i][j-1] + p[i][j];
} else if (j==0) {
p2[i][j] = p2[i-1][j] + p[i][j];
} else {
p2[i][j] = max(p2[i-1][j], p2[i][j-1]) + p[i][j];
}
}
}
return p2[x][y];
}
EDIT:
If you still want a recursive solution, you can do the following. This only makes recursive calls if the necessary values have not yet been computed.
long f(int x,int y,int **p)
{
static long**p2=NULL;
int i, j;
if (!p2) {
p2 = malloc(sizeof(long*)*(x+1));
for (i=0;i<=x;i++) {
p2[i] = malloc(sizeof(long)*(y+1));
for (j=0;j<=y;j++) {
p2[i][j] = MIN;
}
}
}
if (x==0 && y==0) {
p2[x][y] = p[x][y];
} else if (x==0) {
if (p2[x][y-1] == MIN) {
p2[x][y-1] = f(x,y-1,p);
}
p2[x][y] = p2[x][y-1] + p[x][y];
} else if (y==0) {
if (p2[x-1][y] == MIN) {
p2[x-1][y] = f(x-1,y,p);
}
p2[x][y] = p2[x-1][y] + p[x][y];
} else {
if (p2[x][y-1] == MIN) {
p2[x][y-1] = f(x,y-1,p);
}
if (p2[x-1][y] == MIN) {
p2[x-1][y] = f(x-1,y,p);
}
p2[x][y] = max(p2[x-1][y], p2[x][y-1]) + p[x][y];
}
return p2[x][y];
}
You don't specify which compiler you are using. Look into how to increase stack size for your program. However, even if you get it to work for n=20, there will be a limit (may not be far from n=20) due to combinatorial explosion as mentioned in previous comment.
For n > 0, each call to f(n) calls f(n-1) twice. So calling f(n) = calling 2*fn(n-1)
For n = 20, that is 2^20 calls. Each call returns a long. If long is 8 bytes = 2^3, then you have at least 2^23 bytes on the stack.
EDIT
Actually, according to the documentation, the linker controls the stack size.
You can try increasing stack size and implement more efficient algorithm as proposed by different answers
To increase stack size with ld (the GNU linker)
--stack reserve
--stack reserve,commit
Specify the number of bytes of memory to reserve (and optionally commit) to be used as stack for this program. The default is 2Mb reserved, 4K committed. [This option is specific to the i386 PE targeted port of the linker]

UVA's 3n+1 wrong answer although the test cases are correct . . .?

UVA problem 100 - The 3n + 1 problem
I have tried all the test cases and no problems are found.
The test cases I checked:
1 10 20
100 200 125
201 210 89
900 1000 174
1000 900 174
999999 999990 259
But why I get wrong answer all the time?
here is my code:
#include "stdio.h"
unsigned long int cycle = 0, final = 0;
unsigned long int calculate(unsigned long int n)
{
if (n == 1)
{
return cycle + 1;
}
else
{
if (n % 2 == 0)
{
n = n / 2;
cycle = cycle + 1;
calculate(n);
}
else
{
n = 3 * n;
n = n + 1;
cycle = cycle+1;
calculate(n);
}
}
}
int main()
{
unsigned long int i = 0, j = 0, loop = 0;
while(scanf("%ld %ld", &i, &j) != EOF)
{
if (i > j)
{
unsigned long int t = i;
i = j;
j = t;
}
for (loop = i; loop <= j; loop++)
{
cycle = 0;
cycle = calculate(loop);
if(cycle > final)
{
final = cycle;
}
}
printf("%ld %ld %ld\n", i, j, final);
final = 0;
}
return 0;
}
The clue is that you receive i, j but it does not say that i < j for all the cases, check for that condition in your code and remember to always print in order:
<i>[space]<j>[space]<count>
If the input is "out of order" you swap the numbers even in the output, when it is clearly stated you should keep the input order.
Don't see how you're test cases actually ever worked; your recursive cases never return anything.
Here's a one liner just for reference
int three_n_plus_1(int n)
{
return n == 1 ? 1 : three_n_plus_1((n % 2 == 0) ? (n/2) : (3*n+1))+1;
}
Not quite sure how your code would work as you toast "cycle" right after calculating it because 'calculate' doesn't have explicit return values for many of its cases ( you should of had compiler warnings to that effect). if you didn't do cycle= of the cycle=calculate( then it might work?
and tying it all together :-
int three_n_plus_1(int n)
{
return n == 1 ? 1 : three_n_plus_1((n % 2 == 0) ? (n/2) : (3*n+1))+1;
}
int max_int(int a, int b) { return (a > b) ? a : b; }
int min_int(int a, int b) { return (a < b) ? a : b; }
int main(int argc, char* argv[])
{
int i,j;
while(scanf("%d %d",&i, &j) == 2)
{
int value, largest_cycle = 0, last = max_int(i,j);
for(value = min_int(i,j); value <= last; value++) largest_cycle = max_int(largest_cycle, three_n_plus_1(value));
printf("%d %d %d\r\n",i, j, largest_cycle);
}
}
Part 1
This is the hailstone sequence, right? You're trying to determine the length of the hailstone sequence starting from a given N. You know, you really should take out that ugly global variable. It's trivial to calculate it recursively:
long int hailstone_sequence_length(long int n)
{
if (n == 1) {
return 1;
} else if (n % 2 == 0) {
return hailstone_sequence_length(n / 2) + 1;
} else {
return hailstone_sequence_length(3*n + 1) + 1;
}
}
Notice how the cycle variable is gone. It is unnecessary, because each call just has to add 1 to the value computed by the recursive call. The recursion bottoms out at 1, and so we count that as 1. All other recursive steps add 1 to that, and so at the end we are left with the sequence length.
Careful: this approach requires a stack depth proportional to the input n.
I dropped the use of unsigned because it's an inappropriate type for doing most math. When you subtract 1 from (unsigned long) 0, you get a large positive number that is one less than a power of two. This is not a sane behavior in most situations (but exactly the right one in a few).
Now let's discuss where you went wrong. Your original code attempts to measure the hailstone sequence length by modifying a global counter called cycle. However, the main function expects calculate to return a value: you have cycle = calculate(...).
The problem is that two of your cases do not return anything! It is undefined behavior to extract a return value from a function that didn't return anything.
The (n == 1) case does return something but it also has a bug: it fails to increment cycle; it just returns cycle + 1, leaving cycle with the original value.
Part 2
Looking at the main. Let's reformat it a little bit.
int main()
{
unsigned long int i=0,j=0,loop=0;
Change these to long. By the way %ld in scanf expects long anyway, not unsigned long.
while (scanf("%ld %ld",&i,&j) != EOF)
Be careful with scanf: it has more return values than just EOF. Scanf will return EOF if it is not able to make a conversion. If it is able to scan one number, but not the second one, it will return 1. Basically a better test here is != 2. If scanf does not return two, something went wrong with the input.
{
if(i > j)
{
unsigned long int t=i;i=j;j=t;
}
for(loop=i;loop<=j;loop++)
{
cycle=0;
cycle=calculate(loop );
if(cycle>final)
{
final=cycle;
}
}
calculate is called hailstone_sequence_length now, and so this block can just have a local variable: { long len = hailstone_sequence_length(loop); if (len > final) final = len; }
Maybe final should be called max_length?
printf("%ld %ld %ld\n",i,j,final);
final=0;
final should be a local variable in this loop since it is separately used for each test case. Then you don't have to remember to set it to 0.
}
return 0;
}

Resources