Simplified vulgar fractions in C - c

"How to write an algorithm that, given a number n, prints out all the simplified vulgar fractions that have the denominator 1..n"
(I hope I could phrase it well, feel free to rephrase.)
Example: If n is 3, the output should be like "1/2 1/3 2/3"
We were talking about this question in the end of the last class. He showed us a solution and asked us to try to understand the code. Here it is:
#include<stdio.h>
void main()
{
int p,m,n,i,j,a,b;
p=7;
m=0;
n=1;
do
{
printf("%d/%d\n",m,n);
i=j=1;
for(b=2; b<=p; b++)
{
a=m*b/n+1;
if(a*j<b*i)
{
i=a;
j=b;
}
}
m=i;
n=j;
}
while(i<j);
}
I'm new to C and just learning to code, to be honest I couldn't figure out what this code does. And it also prints "0/1", I also wonder why that is, I think it shouldn't be printing that.
Here is my elementary approach to this problem:
#include <stdio.h>
int gcd(int a, int b) // Finds the GCD of two numbers.
{
int temp;
while (a) {
temp = a;
a = b % a;
b = temp;
}
return b;
}
int main(void)
{
int i, j;
for (i = 1; i <= 7; i++) // Denominator goes from 1 to 7
for (j = 1; j < i; j++) // Numerator goes from 1 to denominator
if (gcd(i, j) == 1)
printf("%d/%d ", j, i); // If the numerator and the denominator
// are coprimes then print the fraction
return 0;
}
"n" is 7 in both of the codes. I checked the execution times with much bigger numbers and my algorithm is faster than the other one. So I don't understand what the other code is for. Also any suggestions/corrections about my code is appreciated.

Your professor's code looks like it may have been purposely complicated, maybe as a learning exercise. If that's the case, I can't say I agree with the practice.
Your approach of nested for loops is exactly how I would have approached the solution.
And it also prints "0/1", I also wonder why that is, I think it shouldn't be printing that.
Simply put, it prints "0/1" because of this line:
printf("%d/%d\n",m,n);
The values m and n are initialized to 0 and 1 right before the do loop, so on the first pass it prints exactly that.

Your code is better than the first paste in a few ways. The loop over b is a crappy way of trying to find a common prime factor for m and n. But it only runs to b=7, so the first program can print 11/121 and other non-reduced fractions!
If the loop over b were properly coded, it would take O(sqrt(n)) time. Your gcd() (using the Euclidean Algorithm well) has O(log(n)) time.

The other code is exceptionally poorly written. Single-letter variables for non-idiomatic uses? void main()? No comments? Nobody should be expected to understand that code, and especially not learners.
Your code seems pretty competent - it's far clearer and cleaner than the other code and pretty much superior in every way. The only suggestion I would make is, firstly, you should take in N as user input from the console, to make rerunning the program for different values simpler, and you should also comment the GCD function explaining it's operations.

Related

Example 1-7 from The C Programming Language(version 2)

I am having trouble understanding the example 1.7 given in The C Programming Language. The main purpose of this example is to illustrate the use of functions in C. The book describes the following program as such, "Since C has no exponentiation operator like ** of Fortran, let us illustrate the mechanics of function definition by writing a function power(m,n) to raise an integer m to a positive power n. That is, the value of power(2,5) is 32. This function is not a practical exponentiation routine since it handles only positive powers of small integers, but it's good enough for illustration."
This is the block of C code that follows:
#include <stdio.h>
int power(int m, int n); /*function prototype */
int main() {
/* test power function */
int i;
for(i = 0; i < 10;++i)
printf("%d %d %d\n",i,power(2,i),power(-3,i));
return 0;
}
/* power: raise base to the nth power;n >=0 */
int power(int base,int n) {
int i , p;
p = 1;
for(i = 1;i <= n;++i)
p = p * base;
return p;
}
I understand everything up to the power function's code block. What is confusing me is that for loop. I am still learning C(obviously, i'm in the first chapter) but I come from JavaScript. So when I see this for loop I expect the i to need to be 'bound' to something for it to be of us in iteration(similar to the first for loop in the example). But the power function returns p after p = p * base;. It's not returning anything to do with i. So to me, I think, what is the purpose of this for loop? I notice that if I comment out the for loop and remove the integer i then the numbers printed out do not increment except for the numbers within the previous for loop. To me, I expect the int n would need to increment. Not the i.
It appears to me that within the for loop that n is only being used as comparison to i. Is the purpose of the for loop in the power function only to execute p = p * base if i <= n is true? That can't be true because if that was then it would better be server with just an if statement and wouldn't need a ++i to increment.
What am I missing here?
The for-loop in the power function repeatedly multiplies (and updates) p by base. It does this n times -- i is the counter. If an if-statement were used, then you'd only get up to one multiplication.

Why we need recursion?

What is the benefit to use recursion then loop(for,while,do-while)?
Using Recursion(Here i am getting the sum of a given number, suppose number is 5 then 5 + 4 + 3 + 2 + 1:
#include<stdio.h>
int sum(int n);
void main()
{
int sums =0, n;
scanf("%d",&n);
sums = sum(n);
printf("%d",sums);
while (1)
{
}
}
int sum(int n)
{
if (n==1)
return 1;
return n + sum(n-1);
}
Without recursion(Here i am getting the sum of a given number suppose number is 5 then 5 + 4 + 3 + 2 + 1:
#include<stdio.h>
void main()
{
int sum =0, n;
scanf("%d",&n);
for(int i=n;i>=1;i--)
{
sum = sum + i;
}
printf("%d",sum);
while (1)
{
}
}
You can always make a recursive function an iterative one, and vice versa (Turing said).
In some cases, it's better to use recursion (e.g. traveling on a tree), it's more natural to "think recursively" in such cases. However, if using loops isn't more complicated and much more difficult than a recursion, I prefer them.
Recursion is more costly in memory, but sometimes it clearer and a more readable, using loops increases the performance, but recursion can sometimes be better for the programmer (and his performance).
Deciding what to use - recursion or iteration, depends on what you want to implement, and what's more important for you (readability? performance?), asking recursion or iteration is somehow like asking elegance or performance.
recursion can solve all those problems which can be solved by for loop. But it is difficult or quite impossible to solve some problems by for loop. For example 'json' parsing, 'xml' parsing etc. For this kind of problems you should use recursion. Some dynamic problems can be solved easily by recursion instead of for loop. For a hard problem you do not have to think much solving by recursion instead of for loop. You just form the Recurrence_relation.
I prefer loop over recursive functions. See if this helps : Read it

Approaching Dynamic Programming

I have written a small program to calculate the factorial of a number using Dynamic Programming Technique.
#include<stdio.h>
int fact(int n)
{
int f[n],i;
f[0] = 1;
for(i=1;i<=n;i++)
f[i] = i * f[i-1];
return f[n];
}
int main(void)
{
printf("\n Factorial of %d is %d ",5,fact(5));
return 0;
}
Is the approach of memorization correct? Because, dynamic programming involves recursion. But I have not included it here. So I am not sure of my approach.
Yes, your approach of solving the problem is a very simple case of Dynamic Programming, where you store previously solved sub-problems to help you solve the actual problem. While the example you provided would be considered Dynamic Programming, it usually isn't called Memoization
When someone says Memoization, it usually involves in a top-down approach of solving problems, where you assume you have already solved the sub-problems by structuring your program in a way that will solve sub-problems recursively.
You store, or memoize, the results of these sub-problems so that they will not be computed multiple times.
Let me illustrate Memoization through an example:
Here is a simple example of computing the nth Fibonacci of a number:
int fib(int n)
{
if (n <= 1)
return n;
return fib(n-1) + fib(n-2);
}
The above code uses recursion to solve sub-problems (fib(n-1) and fib(n-2)) so that fib(n) can be solved in the end. It assumes that fib(n-1) and fib(n-2) are already solved in the way that it is structured.
Though this code looks elegant, the running time is exponential, because you can solve fib(i), where i is a number less than n, multiple times. You can look at the diagram presented here to see the tree generated by this problem: http://www.geeksforgeeks.org/program-for-nth-fibonacci-number.
To avoid the unnecessary re-computation, Memoization is used to optimizes run-time by using memory.
Here is an optimized example of computing the nth Fibonacci number using Memoization:
/*Global array initialized to 0*/
int a[100];
int fib(int n)
{
/*base case*/
if (n <= 1)
return n;
/*if fib(n) has not been computed, compute it*/
if (a[n] == 0) {
a[n] = fib(n - 1) + fib(n - 2);
}
*/Otherwise, simply get a[n] and return it*/
return a[n];
}
As you can see, the overall structure is not that much different from the recursive solution, but it runs linear time instead of exponential time because fib(i) will only be computed only if we have not computed already.
If I were to use your approach, Dynamic Programming, for the Fibonacci problem, it would look something like this:
int fib(int n)
{
/* just like the array you declared in your solution */
int f[n+1];
int i;
/* set up the base cases, just like how you set f[0] to 1*/
f[0] = 0;
f[1] = 1;
for (i = 2; i <= n; i++)
{
/* using previously solved problem to solve further problems*/
f[i] = f[i-1] + f[i-2];
}
/*return the final result*/
return f[n];
}
There are more subtle differences, trade offs, and implications between Dynamic Programming and Memoization. Some consider Memoization a subset of Dynamic Programming. You can read more about the difference here:
Dynamic programming and memoization: bottom-up vs top-down approaches
Yes this is dynamic programming : going from base cases up to final case. Of course your example (factorial) is too simple so you have been able to simplify many things by yourself : you eliminated the recursion and never use a test in the memoization. But anyway that's it.
For the general scheme of memoization see http://en.wikipedia.org/wiki/Memoization.
For explanation about Dynamic programming see http://en.wikipedia.org/wiki/Dynamic_programming, you will be able to read the section about Fibonacci sequence and its computation using a bottom-up approach.

C clarification on recursive functions

Hello getting better at C everyday, this is a example problem out of my textbook that generates fibonacci numbers and shows recursive functions. The program works but I just don't understand how... Specifically in parts (looper % 5), the whole functionfib and what printf(", %8ld", fib(looper)); is doing. Is it like saying fib() do x amount of times. If this problem is not easy to explain then could someone show me a easier way to understand how recursive functions work other then "towers of hanoi" example. Thanks.
NOTE: program is meant to handle up to 30 numbers others wise it starts to look ugly.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
long fib (long num);
int main(void)
{
int seriesSize;
printf("This program will print out a Fibonacci series.\n");
printf("How many many numers do you wnat? ");
scanf_s("%d", &seriesSize);
printf("First %d Fib numbers: \n", seriesSize);
for (int looper = 0; looper < seriesSize; looper++)
{
if (looper % 5)
{
printf(", %8ld", fib(looper));
}
else
{
printf("\n%8ld", fib(looper));
}
}
printf("\n");
return 0;
}
long fib(long num)
{
if (num == 0 || num == 1)
{
return num;
}
return (fib(num - 1) + fib(num - 2));
}
The idea behind the long fib(long num) function is that it mirrors the natural definition of the fibonacci sequence in that it is defined in terms of itself. That is, fib(n) is defined by fib(n-1) and fib(n-2). For example, fib(5) is fib(4)+fib(3).
The textbook has written the function in a recursive manner as described above. Note that this is not the most efficient way to implement a fibonacci function but it does logically make sense.
To understand it, it pays to trace through its execution with an example input. Take fib(3) for example. The first if statement doesn't trigger because num is not 0 or 1. Thus, it works out what fib(2) and fib(1) are, and adds them together. We know what fib(1) does - it returns 1 in the first if statement. Trace through fib(2) in a similar manner and you'll see that it returns 1. Thus, fib(3) will return fib(2)+fib(1)=2. You can extend this further - take fib(4). It will return fib(3)+fib(2), which we know are 2 and 1, hence fib(4) = 3.
This approach can be taken for most recursive functions - think of it as creating new instances of the fib() function which continually creates until it "bottoms out" at an end case (num == 1 or num == 0, in this case), and returns back up, filling in the answers until you get back to the function you started with, with the answer.

why runtime error on online judge?

I am unable to understand why i am getting runtime error with this code. Problem is every number >=6 can be represented as sum of two prime numbers.
My code is ...... Thanks in advance problem link is http://poj.org/problem?id=2262
#include "stdio.h"
#include "stdlib.h"
#define N 1000000
int main()
{
long int i,j,k;
long int *cp = malloc(1000000*sizeof(long int));
long int *isprime = malloc(1000000*sizeof(long int));
//long int *isprime;
long int num,flag;
//isprime = malloc(2*sizeof(long int));
for(i=0;i<N;i++)
{
isprime[i]=1;
}
j=0;
for(i=2;i<N;i++)
{
if(isprime[i])
{
cp[j] = i;
j++;
for(k=i*i;k<N;k+=i)
{
isprime[k] = 0;
}
}
}
//for(i=0;i<j;i++)
//{
// printf("%d ",cp[i]);
//}
//printf("\n");
while(1)
{
scanf("%ld",&num);
if(num==0) break;
flag = 0;
for(i=0;i<j&&num>cp[i];i++)
{
//printf("%d ",cp[i]);
if(isprime[num-cp[i]])
{
printf("%ld = %ld + %ld\n",num,cp[i],num-cp[i]);
flag = 1;
break;
}
}
if(flag==0)
{
printf("Goldbach's conjecture is wrong.\n");
}
}
free(cp);
free(isprime);
return 0;
}
Two possibilities immediately spring to mind. The first is that the user input may be failing if whatever test harness is being used does not provide any input. Without knowing more detail on the harness, this is a guess at best.
You could check that by hard-coding a value rather than accepting one from standard input.
The other possibility is the rather large memory allocations being done. It may be that you're in a constrained environment which doesn't allow that.
A simple test for that is to drop the value of N (and, by the way, use it rather than the multiple hardcoded 1000000 figures in your malloc calls). A better way would be to check the return value from malloc to ensure it's not NULL. That should be done anyway.
And, aside from that, you may want to check your Eratosthenes Sieve code. The first item that should be marked non-prime for the prime i is i + i rather than i * i as you have. I think it should be:
for (k = i + i; k < N; k += i)
The mathematical algorithm is actually okay since any multiple of N less than N * N will already have been marked non-prime by virtue of the fact it's a multiple of one of the primes previously checked.
Your problem lies with integer overflow. At the point where N becomes 46_349, N * N is 2_148_229_801 which, if you have a 32-bit two's complement integer (maximum value of 2_147_483_647), will wrap around to -2_146_737_495.
When that happens, the loop keeps going since that negative number is still less than your limit, but using it as an array index is, shall we say, inadvisable :-)
The reason it works with i + i is because your limit is well short of INT_MAX / 2 so no overflow happens there.
If you want to make sure that this won't be a problem if you get up near INT_MAX / 2, you can use something like:
for (k = i + i; (k < N) && (k > i); k += i)
That extra check on k should catch the wraparound event, provided your wrapping follows the "normal" behaviour - technically, I think it's undefined behaviour to wrap but most implementations simply wrap two positives back to a negative due to the two's complement nature. Be aware then that this is actually non-portable, but what that means in practice is that it will only work on 99.999% of machines out there :-)
But, if you're a stickler for portability, there are better ways to prevent overflow in the first place. I won't go into them here but to say they involve subtracting one of the terms being summed from MAX_INT and comparing it to the other term being summed.
The only way I can get this to give an error is if I enter a value greater than 1000000 or less than 1 to the scanf().
Like this:
ubuntu#amrith:/tmp$ ./x
183475666
Segmentation fault (core dumped)
ubuntu#amrith:/tmp$
But the reason for that should be obvious. Other than that, this code looks good.
Just trying to find what went wrong!
If the sizeof(long int) is 4 bytes for the OS that you are using, then it makes this problem.
In the code:
for(k=i*i;k<N;k+=i)
{
isprime[k] = 0;
}
Here, when you do k = i*i, for large values if i, the value of k goes beyond 4 bytesand get truncated which may result in negative numbers and so, the condition k<N is satisfied but with a negative number :). So you get a segmentation fault there.
It's good that you need only i+i, but if you need to increase the limit, take care of this problem.

Resources